int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) { VSILFILE* fp = VSIFileFromMemBuffer( "/vsimem/test.tar", reinterpret_cast<GByte*>(const_cast<uint8_t*>(buf)), len, FALSE ); VSIFCloseL(fp); CPLPushErrorHandler(CPLQuietErrorHandler); char** papszArgv = nullptr; // Prevent generating too big output raster. Make sure they are set at // the beginning to avoid being accidentally eaten by invalid arguments // afterwards. papszArgv = CSLAddString(papszArgv, "-limit_outsize"); papszArgv = CSLAddString(papszArgv, "1000000"); fp = VSIFOpenL("/vsitar//vsimem/test.tar/cmd.txt", "rb"); if( fp != nullptr ) { const char* pszLine = nullptr; while( (pszLine = CPLReadLineL(fp)) != nullptr ) { if( !EQUAL(pszLine, "-limit_outsize") ) papszArgv = CSLAddString(papszArgv, pszLine); } VSIFCloseL(fp); } int nXDim = -1; int nYDim = -1; bool bXDimPct = false; bool bYDimPct = false; bool bNonNearestResampling = false; int nBlockXSize = 0; int nBlockYSize = 0; bool bStatsEnabled = false; bool bHFA = false; if( papszArgv != nullptr ) { int nCount = CSLCount(papszArgv); for( int i = 0; i < nCount; i++ ) { if( EQUAL(papszArgv[i], "-outsize") && i + 2 < nCount ) { nXDim = atoi(papszArgv[i+1]); bXDimPct = (papszArgv[i+1][0] != '\0' && papszArgv[i+1][strlen(papszArgv[i+1])-1] == '%'); nYDim = atoi(papszArgv[i+2]); bYDimPct = (papszArgv[i+2][0] != '\0' && papszArgv[i+2][strlen(papszArgv[i+2])-1] == '%'); } else if( EQUAL(papszArgv[i], "-r") && i + 1 < nCount ) { bNonNearestResampling = !STARTS_WITH_CI(papszArgv[i+1], "NEAR"); } else if( EQUAL(papszArgv[i], "-co") && i + 1 < nCount ) { if( STARTS_WITH_CI(papszArgv[i+1], "BLOCKSIZE=") ) { nBlockXSize = std::max(nBlockXSize, atoi(papszArgv[i+1]+strlen("BLOCKSIZE="))); nBlockYSize = std::max(nBlockYSize, atoi(papszArgv[i+1]+strlen("BLOCKSIZE="))); } else if( STARTS_WITH_CI(papszArgv[i+1], "BLOCKXSIZE=") ) { nBlockXSize = std::max(nBlockXSize, atoi(papszArgv[i+1]+strlen("BLOCKXSIZE="))); } else if( STARTS_WITH_CI(papszArgv[i+1], "BLOCKYSIZE=") ) { nBlockYSize = std::max(nBlockYSize, atoi(papszArgv[i+1]+strlen("BLOCKYSIZE="))); } } else if( EQUAL(papszArgv[i], "-stats") ) { bStatsEnabled = true; } else if( EQUAL(papszArgv[i], "-of") && i + 1 < nCount ) { bHFA = EQUAL( papszArgv[i+1], "HFA" ); } } if( bHFA ) { // Disable statistics computation for HFA, as it can be time // consuming. // See https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=10067 papszArgv = CSLInsertString(papszArgv, 0, "-co"); papszArgv = CSLInsertString(papszArgv, 1, "STATISTICS=NO"); } } if( papszArgv != nullptr ) { GDALTranslateOptions* psOptions = GDALTranslateOptionsNew(papszArgv, nullptr); if( psOptions ) { GDALDatasetH hSrcDS = GDALOpen( "/vsitar//vsimem/test.tar/in", GA_ReadOnly ); if( hSrcDS != nullptr ) { // Also check that reading the source doesn't involve too // much memory GDALDataset* poSrcDS = reinterpret_cast<GDALDataset*>(hSrcDS); int nBands = poSrcDS->GetRasterCount(); if( nBands < 10 ) { // Prevent excessive downsampling which might require huge // memory allocation bool bOKForResampling = true; if( bNonNearestResampling && nXDim >= 0 && nYDim >= 0 ) { if( bXDimPct && nXDim > 0 ) { nXDim = static_cast<int>( poSrcDS->GetRasterXSize() / 100.0 * nXDim); } if( bYDimPct && nYDim > 0 ) { nYDim = static_cast<int>( poSrcDS->GetRasterYSize() / 100.0 * nYDim); } if( nXDim > 0 && poSrcDS->GetRasterXSize() / nXDim > 100 ) bOKForResampling = false; if( nYDim > 0 && poSrcDS->GetRasterYSize() / nYDim > 100 ) bOKForResampling = false; } bool bOKForSrc = true; if( nBands ) { const int nDTSize = GDALGetDataTypeSizeBytes( poSrcDS->GetRasterBand(1)->GetRasterDataType() ); vsi_l_offset nSize = static_cast<vsi_l_offset>(nBands) * poSrcDS->GetRasterXSize() * poSrcDS->GetRasterYSize() * nDTSize; if( nSize > 10 * 1024 * 1024 ) { bOKForSrc = false; } int nBXSize = 0, nBYSize = 0; GDALGetBlockSize( GDALGetRasterBand(hSrcDS, 1), &nBXSize, &nBYSize ); const char* pszInterleave = GDALGetMetadataItem( hSrcDS, "INTERLEAVE", "IMAGE_STRUCTURE" ); int nSimultaneousBands = (pszInterleave && EQUAL(pszInterleave, "PIXEL")) ? nBands : 1; if( static_cast<GIntBig>(nSimultaneousBands)* nBXSize * nBYSize * nDTSize > 10 * 1024 * 1024 ) { bOKForSrc = false; } if( static_cast<GIntBig>(nBlockXSize) * nBlockYSize > 10 * 1024 * 1024 / (nBands * nDTSize) ) { bOKForSrc = false; } } bool bOKForStats = true; if( nBands && bStatsEnabled ) { // Other types might be too slow with sanitization enabled // See https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=10029 bOKForStats = poSrcDS->GetRasterBand(1)->GetRasterDataType() == GDT_Byte; } if( bOKForSrc && bOKForResampling && bOKForStats ) { GDALDatasetH hOutDS = GDALTranslate("/vsimem/out", hSrcDS, psOptions, nullptr); if( hOutDS ) GDALClose(hOutDS); } } GDALClose(hSrcDS); } GDALTranslateOptionsFree(psOptions); } } CSLDestroy(papszArgv); VSIRmdirRecursive("/vsimem/"); CPLPopErrorHandler(); return 0; }
CPLErr GDALWMSMiniDriver_TiledWMS::Initialize(CPLXMLNode *config, CPL_UNUSED char **OpenOptions) { CPLErr ret = CE_None; CPLXMLNode *tileServiceConfig=NULL; CPLHTTPResult *psResult=NULL; CPLXMLNode *TG=NULL; char **requests=NULL; char **substs=NULL; char **keys=NULL; for (int once=1;once;once--) { // Something to break out of // Parse info from the service m_end_url = CPLGetXMLValue(config,"AdditionalArgs",""); m_base_url = CPLGetXMLValue(config, "ServerURL", ""); if (m_base_url.empty()) { CPLError(ret=CE_Failure, CPLE_AppDefined, "%s ServerURL missing.",SIG); break; } CPLString tiledGroupName (CPLGetXMLValue(config, "TiledGroupName", "")); if (tiledGroupName.empty()) { CPLError(ret=CE_Failure, CPLE_AppDefined, "%s TiledGroupName missing.",SIG); break; } // Change strings, key is an attribute, value is the value of the Change node // Multiple substitutions are possible TG=CPLSearchXMLNode(config, "Change"); while(TG!=NULL) { CPLString name=CPLGetXMLValue(TG,"key",""); if (name.empty()) { CPLError(ret=CE_Failure, CPLE_AppDefined, "%s Change element needs a non-empty \"key\" attribute",SIG); break; } substs=CSLSetNameValue(substs,name,CPLGetXMLValue(TG,"","")); TG=SearchXMLSiblings(TG,"Change"); } if (ret!=CE_None) break; CPLString getTileServiceUrl = m_base_url + "request=GetTileService"; psResult = CPLHTTPFetch(getTileServiceUrl, NULL); if (NULL==psResult) { CPLError(ret=CE_Failure, CPLE_AppDefined, "%s Can't use HTTP", SIG); break; } if ((psResult->nStatus!=0)||(NULL==psResult->pabyData)||('\0'==psResult->pabyData[0])) { CPLError(ret=CE_Failure, CPLE_AppDefined, "%s Server response error on GetTileService.",SIG); break; } if (NULL==(tileServiceConfig=CPLParseXMLString((const char*)psResult->pabyData))) { CPLError(ret=CE_Failure,CPLE_AppDefined, "%s Error parsing the GetTileService response.",SIG); break; } if (NULL==(TG=CPLSearchXMLNode(tileServiceConfig, "TiledPatterns"))) { CPLError(ret=CE_Failure,CPLE_AppDefined, "%s Can't locate TiledPatterns in server response.",SIG); break; } // Get the global base_url and bounding box, these can be overwritten at the tileGroup level // They are just pointers into existing structures, cleanup is not required const char *global_base_url=CPLGetXMLValue(tileServiceConfig,"TiledPatterns.OnlineResource.xlink:href",""); CPLXMLNode *global_latlonbbox=CPLGetXMLNode(tileServiceConfig, "TiledPatterns.LatLonBoundingBox"); CPLXMLNode *global_bbox=CPLGetXMLNode(tileServiceConfig, "TiledPatterns.BoundingBox"); if (NULL==(TG=SearchLeafGroupName(TG->psChild,tiledGroupName))) { CPLError(ret=CE_Failure,CPLE_AppDefined, "%s Can't locate TiledGroup ""%s"" in server response.",SIG, tiledGroupName.c_str()); break; } int band_count=atoi(CPLGetXMLValue(TG, "Bands", "3")); if (!GDALCheckBandCount(band_count, FALSE)) { CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG, "Invalid number of bands in server response"); break; } // Collect all keys defined by this tileset if (NULL!=CPLGetXMLNode(TG,"Key")) { CPLXMLNode *node=CPLGetXMLNode(TG,"Key"); while (NULL!=node) { const char *val=CPLGetXMLValue(node,NULL,NULL); if (val) keys=CSLAddString(keys,val); node=SearchXMLSiblings(node,"Key"); } } // Data values are attributes, they include NoData Min and Max if (NULL!=CPLGetXMLNode(TG,"DataValues")) { const char *nodata=CPLGetXMLValue(TG,"DataValues.NoData",NULL); if (nodata!=NULL) m_parent_dataset->WMSSetNoDataValue(nodata); const char *min=CPLGetXMLValue(TG,"DataValues.min",NULL); if (min!=NULL) m_parent_dataset->WMSSetMinValue(min); const char *max=CPLGetXMLValue(TG,"DataValues.max",NULL); if (max!=NULL) m_parent_dataset->WMSSetMaxValue(max); } m_parent_dataset->WMSSetBandsCount(band_count); m_parent_dataset->WMSSetDataType(GDALGetDataTypeByName(CPLGetXMLValue(TG, "DataType", "Byte"))); m_projection_wkt=CPLGetXMLValue(TG, "Projection",""); m_base_url=CPLGetXMLValue(TG,"OnlineResource.xlink:href",global_base_url); if (m_base_url[0]=='\0') { CPLError(ret=CE_Failure,CPLE_AppDefined, "%s%s",SIG, "Can't locate OnlineResource in the server response."); break; } // Bounding box, local, global, local lat-lon, global lat-lon, in this order CPLXMLNode *bbox = CPLGetXMLNode(TG, "BoundingBox"); if (NULL==bbox) bbox=global_bbox; if (NULL==bbox) bbox=CPLGetXMLNode(TG, "LatLonBoundingBox"); if (NULL==bbox) bbox=global_latlonbbox; if (NULL==bbox) { CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG, "Can't locate the LatLonBoundingBox in server response."); break; } m_data_window.m_x0=CPLAtof(CPLGetXMLValue(bbox,"minx","0")); m_data_window.m_x1=CPLAtof(CPLGetXMLValue(bbox,"maxx","-1")); m_data_window.m_y0=CPLAtof(CPLGetXMLValue(bbox,"maxy","0")); m_data_window.m_y1=CPLAtof(CPLGetXMLValue(bbox,"miny","-1")); if ((m_data_window.m_x1-m_data_window.m_x0)<0) { CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s", SIG, "Coordinate order in BBox, problem in server response"); break; } // Is there a palette? // // Format is // <Palette> // <Size>N</Size> : Optional // <Model>RGBA|RGB|CMYK|HSV|HLS|L</Model> :mandatory // <Entry idx=i c1=v1 c2=v2 c3=v3 c4=v4/> :Optional // <Entry .../> // </Palette> // the idx attribute is optional, it autoincrements // The entries are actually vertices, interpolation takes place inside // The palette starts initialized with zeros // HSV and HLS are the similar, with c2 and c3 swapped // RGB or RGBA are same // GDALColorTable *poColorTable=NULL; if ((band_count==1) && CPLGetXMLNode(TG,"Palette")) { CPLXMLNode *node=CPLGetXMLNode(TG,"Palette"); int entries=static_cast<int>(getXMLNum(node,"Size","255")); GDALPaletteInterp eInterp=GPI_RGB; CPLString pModel=CPLGetXMLValue(node,"Model","RGB"); if (!pModel.empty() && pModel.find("RGB")!=std::string::npos) eInterp=GPI_RGB; else { CPLError(CE_Failure, CPLE_AppDefined, "%s Palette Model %s is unknown, use RGB or RGBA", SIG, pModel.c_str()); return CE_Failure; } if ((entries>0)&&(entries<257)) { int start_idx, end_idx; GDALColorEntry ce_start={0,0,0,255},ce_end={0,0,0,255}; // Create it and initialize it to nothing poColorTable = new GDALColorTable(eInterp); poColorTable->CreateColorRamp(0,&ce_start,entries-1,&ce_end); // Read the values CPLXMLNode *p=CPLGetXMLNode(node,"Entry"); if (p) { // Initialize the first entry start_idx=static_cast<int>(getXMLNum(p,"idx","0")); ce_start=GetXMLColorEntry(p); if (start_idx<0) { CPLError(CE_Failure, CPLE_AppDefined, "%s Palette index %d not allowed",SIG,start_idx); delete poColorTable; return CE_Failure; } poColorTable->SetColorEntry(start_idx,&ce_start); while (NULL!=(p=SearchXMLSiblings(p,"Entry"))) { // For every entry, create a ramp ce_end=GetXMLColorEntry(p); end_idx=static_cast<int>(getXMLNum(p,"idx",CPLString().FormatC(start_idx+1).c_str())); if ((end_idx<=start_idx)||(start_idx>=entries)) { CPLError(CE_Failure, CPLE_AppDefined, "%s Index Error at index %d",SIG,end_idx); delete poColorTable; return CE_Failure; } poColorTable->CreateColorRamp(start_idx,&ce_start, end_idx,&ce_end); ce_start=ce_end; start_idx=end_idx; } } m_parent_dataset->SetColorTable(poColorTable); } else { CPLError(CE_Failure, CPLE_AppDefined,"%s Palette definition error",SIG); return CE_Failure; } } int overview_count=0; CPLXMLNode *Pattern=TG->psChild; m_bsx=m_bsy=-1; m_data_window.m_sx=m_data_window.m_sy=0; for (int once2=1;once2;once2--) { // Something to break out of while ((NULL!=Pattern)&&(NULL!=(Pattern=SearchXMLSiblings(Pattern,"=TilePattern")))) { int mbsx,mbsy; CPLString request; FindChangePattern(Pattern->psChild->pszValue,substs,keys,request); char **papszTokens=CSLTokenizeString2(request,"&",0); const char* pszWIDTH = CSLFetchNameValue(papszTokens,"WIDTH"); const char* pszHEIGHT = CSLFetchNameValue(papszTokens,"HEIGHT"); if (pszWIDTH == NULL || pszHEIGHT == NULL) { CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG, "Cannot find width and/or height parameters."); overview_count=0; CSLDestroy(papszTokens); break; } mbsx=atoi(pszWIDTH); mbsy=atoi(pszHEIGHT); if (m_projection_wkt.empty()) { m_projection_wkt = CSLFetchNameValueDef(papszTokens,"SRS", ""); if (!m_projection_wkt.empty()) m_projection_wkt=ProjToWKT(m_projection_wkt); } if (-1==m_bsx) m_bsx=mbsx; if (-1==m_bsy) m_bsy=mbsy; if ((m_bsx!=mbsx)||(m_bsy!=mbsy)) { CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG, "Tileset uses different block sizes."); overview_count=0; CSLDestroy(papszTokens); break; } double x,y,X,Y; if (CPLsscanf(CSLFetchNameValueDef(papszTokens,"BBOX", ""),"%lf,%lf,%lf,%lf",&x,&y,&X,&Y)!=4) { CPLError(ret=CE_Failure,CPLE_AppDefined, "%s Error parsing BBOX, pattern %d\n",SIG,overview_count+1); CSLDestroy(papszTokens); break; } // Pick the largest size int sx=static_cast<int>((m_data_window.m_x1-m_data_window.m_x0)/(X-x)*m_bsx); int sy=static_cast<int>(fabs((m_data_window.m_y1-m_data_window.m_y0)/(Y-y)*m_bsy)); if (sx>m_data_window.m_sx) m_data_window.m_sx=sx; if (sy>m_data_window.m_sy) m_data_window.m_sy=sy; CSLDestroy(papszTokens); // Only use overlays where the top coordinate is within a pixel from the top of coverage double pix_off,temp; pix_off=m_bsy*modf(fabs((Y-m_data_window.m_y0)/(Y-y)),&temp); if ((pix_off<1)||((m_bsy-pix_off)<1)) { requests=CSLAddString(requests,request); overview_count++; } else CPLError(CE_Warning,CPLE_AppDefined, "%s Overlay size %dX%d can't be used due to alignment",SIG,sx,sy); Pattern=Pattern->psNext; } // The tlevel is needed, the tx and ty are not used by this minidriver m_data_window.m_tlevel = 0; m_data_window.m_tx = 0; m_data_window.m_ty = 0; // Make sure the parent_dataset values are set before creating the bands m_parent_dataset->WMSSetBlockSize(m_bsx,m_bsy); m_parent_dataset->WMSSetRasterSize(m_data_window.m_sx,m_data_window.m_sy); m_parent_dataset->WMSSetDataWindow(m_data_window); //m_parent_dataset->WMSSetOverviewCount(overview_count); m_parent_dataset->WMSSetClamp(false); // Ready for the Rasterband creation for (int i=0;i<overview_count;i++) { CPLString request=GetLowestScale(requests,i); double scale=Scale(request); // Base scale should be very close to 1 if ((0==i)&&(fabs(scale-1) > 1e-6)) { CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG, "Base resolution pattern missing."); break; } // Prepare the request and insert it back into the list // Find returns an answer relative to the original string start! size_t startBbox=FindBbox(request); size_t endBbox=request.find('&',startBbox); if (endBbox==std::string::npos) endBbox=request.size(); request.replace(startBbox,endBbox-startBbox,"${GDAL_BBOX}"); requests = CSLInsertString(requests,i,request); // Create the Rasterband or overview for (int j = 1; j <= band_count; j++) { if (i!=0) m_parent_dataset->mGetBand(j)->AddOverview(scale); else { // Base resolution GDALWMSRasterBand *band=new GDALWMSRasterBand(m_parent_dataset,j,1); if (poColorTable!=NULL) band->SetColorInterpretation(GCI_PaletteIndex); else band->SetColorInterpretation(BandInterp(band_count,j)); m_parent_dataset->mSetBand(j, band); }; } } if ((overview_count==0)||(m_bsx<1)||(m_bsy<1)) { CPLError(ret=CE_Failure,CPLE_AppDefined, "%s No usable TilePattern elements found",SIG); break; } } } CSLDestroy(keys); CSLDestroy(substs); if (tileServiceConfig) CPLDestroyXMLNode(tileServiceConfig); if (psResult) CPLHTTPDestroyResult(psResult); m_requests=requests; return ret; }