Merge branch 'master' into adsk-contrib-fix-std-stream-overflow

pull/1486/head
Kim Kulling 2017-10-14 21:47:21 +02:00 committed by GitHub
commit 44ad80201c
15 changed files with 664 additions and 29 deletions

View File

@ -26,6 +26,10 @@ function generate()
OPTIONS="$OPTIONS -DASSIMP_ASAN=OFF"
fi
if [ "$UBSAN" = "ON" ] ; then
OPTIONS="$OPTIONS -DASSIMP_UBSAN=ON"
fi
cmake -G "Unix Makefiles" $OPTIONS
}

View File

@ -46,6 +46,9 @@ matrix:
- os: linux
compiler: clang
env: ASAN=ON
- os: linux
compiler: clang
env: UBSAN=ON
- os: linux
compiler: clang
env: SHARED_BUILD=ON

View File

@ -86,6 +86,10 @@ OPTION ( ASSIMP_ASAN
"Enable AddressSanitizer."
OFF
)
OPTION ( ASSIMP_UBSAN
"Enable Undefined Behavior sanitizer."
OFF
)
OPTION ( SYSTEM_IRRXML
"Use system installed Irrlicht/IrrXML library."
OFF
@ -234,6 +238,12 @@ if (ASSIMP_ASAN)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
endif()
if (ASSIMP_UBSAN)
MESSAGE(STATUS "Undefined Behavior sanitizer enabled")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all")
endif()
INCLUDE (FindPkgMacros)
INCLUDE (PrecompiledHeader)

View File

@ -171,7 +171,8 @@ int B3DImporter::ReadByte(){
// ------------------------------------------------------------------------------------------------
int B3DImporter::ReadInt(){
if( _pos+4<=_buf.size() ){
int n=*(int*)&_buf[_pos];
int n;
memcpy(&n, &_buf[_pos], 4);
_pos+=4;
return n;
}
@ -182,7 +183,8 @@ int B3DImporter::ReadInt(){
// ------------------------------------------------------------------------------------------------
float B3DImporter::ReadFloat(){
if( _pos+4<=_buf.size() ){
float n=*(float*)&_buf[_pos];
float n;
memcpy(&n, &_buf[_pos], 4);
_pos+=4;
return n;
}

View File

@ -589,7 +589,10 @@ template <> inline void Structure :: Convert<short> (short& dest,const FileData
{
// automatic rescaling from short to float and vice versa (seems to be used by normals)
if (name == "float") {
dest = static_cast<short>(db.reader->GetF4() * 32767.f);
float f = db.reader->GetF4();
if ( f > 1.0f )
f = 1.0f;
dest = static_cast<short>( f * 32767.f);
//db.reader->IncPtr(-4);
return;
}

View File

@ -151,7 +151,8 @@ uint32_t ReadWord(const char* input, const char*& cursor, const char* end)
TokenizeError("cannot ReadWord, out of bounds",input, cursor);
}
uint32_t word = *reinterpret_cast<const uint32_t*>(cursor);
uint32_t word;
memcpy(&word, cursor, 4);
AI_SWAP4(word);
cursor += k_to_read;

View File

@ -272,7 +272,6 @@ bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const
const IfcVector3& b0 = boundary[i];
const IfcVector3& b1 = boundary[(i + 1) % bcount];
IfcVector3 b = b1 - b0;
IfcFloat b_sqlen_inv = 1.0 / b.SquareLength();
// segment-segment intersection
// solve b0 + b*s = e0 + e*t for (s,t)
@ -281,6 +280,7 @@ bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const
// no solutions (parallel lines)
continue;
}
IfcFloat b_sqlen_inv = 1.0 / b.SquareLength();
const IfcFloat x = b0.x - e0.x;
const IfcFloat y = b0.y - e0.y;

View File

@ -1,7 +1,9 @@
#include "jassimp.h"
#include <assimp/cimport.h>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/IOStream.hpp>
#include <assimp/IOSystem.hpp>
#ifdef JNI_LOG
@ -12,9 +14,11 @@
#define lprintf(...) printf (__VA_ARGS__)
#endif /* ANDROID */
#else
#define lprintf
#define lprintf
#endif
static std::string gLastErrorString;
// Automatically deletes a local ref when it goes out of scope
class SmartLocalRef {
private:
@ -270,6 +274,81 @@ static bool callv(JNIEnv *env, jobject object, const char* typeName,
return true;
}
static jobject callo(JNIEnv *env, jobject object, const char* typeName, const char* methodName,
const char* signature,/* const*/ jvalue* params)
{
jclass clazz = env->FindClass(typeName);
SmartLocalRef clazzRef(env, clazz);
if (NULL == clazz)
{
lprintf("could not find class %s\n", typeName);
return NULL;
}
jmethodID mid = env->GetMethodID(clazz, methodName, signature);
if (NULL == mid)
{
lprintf("could not find method %s with signature %s in type %s\n", methodName, signature, typeName);
return NULL;
}
jobject jReturnValue = env->CallObjectMethodA(object, mid, params);
return jReturnValue;
}
static int calli(JNIEnv *env, jobject object, const char* typeName, const char* methodName,
const char* signature)
{
jclass clazz = env->FindClass(typeName);
SmartLocalRef clazzRef(env, clazz);
if (NULL == clazz)
{
lprintf("could not find class %s\n", typeName);
return false;
}
jmethodID mid = env->GetMethodID(clazz, methodName, signature);
if (NULL == mid)
{
lprintf("could not find method %s with signature %s in type %s\n", methodName, signature, typeName);
return false;
}
jint jReturnValue = env->CallIntMethod(object, mid);
return (int) jReturnValue;
}
static int callc(JNIEnv *env, jobject object, const char* typeName, const char* methodName,
const char* signature)
{
jclass clazz = env->FindClass(typeName);
SmartLocalRef clazzRef(env, clazz);
if (NULL == clazz)
{
lprintf("could not find class %s\n", typeName);
return false;
}
jmethodID mid = env->GetMethodID(clazz, methodName, signature);
if (NULL == mid)
{
lprintf("could not find method %s with signature %s in type %s\n", methodName, signature, typeName);
return false;
}
jint jReturnValue = env->CallCharMethod(object, mid);
return (int) jReturnValue;
}
static bool callStaticObject(JNIEnv *env, const char* typeName, const char* methodName,
const char* signature,/* const*/ jvalue* params, jobject& returnValue)
@ -359,6 +438,158 @@ static bool copyBufferArray(JNIEnv *env, jobject jMesh, const char* jBufferName,
return true;
}
class JavaIOStream : public Assimp::IOStream
{
private:
size_t pos;
size_t size;
char* buffer;
jobject jIOStream;
public:
JavaIOStream(size_t size, char* buffer, jobject jIOStream) :
pos(0),
size(size),
buffer(buffer),
jIOStream(jIOStream)
{};
~JavaIOStream(void)
{
free(buffer);
};
size_t Read(void* pvBuffer, size_t pSize, size_t pCount)
{
const size_t cnt = std::min(pCount,(size - pos)/pSize);
const size_t ofs = pSize*cnt;
memcpy(pvBuffer, buffer + pos, ofs);
pos += ofs;
return cnt;
};
size_t Write(const void* pvBuffer, size_t pSize, size_t pCount)
{
return 0;
};
aiReturn Seek(size_t pOffset, aiOrigin pOrigin)
{
if (aiOrigin_SET == pOrigin) {
if (pOffset >= size) {
return AI_FAILURE;
}
pos = pOffset;
}
else if (aiOrigin_END == pOrigin) {
if (pOffset >= size) {
return AI_FAILURE;
}
pos = size-pOffset;
}
else {
if (pOffset + pos >= size) {
return AI_FAILURE;
}
pos += pOffset;
}
return AI_SUCCESS;
};
size_t Tell(void) const
{
return pos;
};
size_t FileSize() const
{
return size;
};
void Flush() {};
jobject javaObject()
{
return jIOStream;
};
};
class JavaIOSystem : public Assimp::IOSystem {
private:
JNIEnv* mJniEnv;
jobject& mJavaIOSystem;
public:
JavaIOSystem(JNIEnv* env, jobject& javaIOSystem) :
mJniEnv(env),
mJavaIOSystem(javaIOSystem)
{};
bool Exists( const char* pFile) const
{
jvalue params[1];
params[0].l = mJniEnv->NewStringUTF(pFile);
return call(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "exists", "(Ljava/lang/String;)Z", params);
};
char getOsSeparator() const
{
return (char) callc(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "getOsSeparator", "()C");
};
Assimp::IOStream* Open(const char* pFile,const char* pMode = "rb")
{
jvalue params[2];
params[0].l = mJniEnv->NewStringUTF(pFile);
params[1].l = mJniEnv->NewStringUTF(pMode);
jobject jStream = callo(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "open", "(Ljava/lang/String;Ljava/lang/String;)Ljassimp/AiIOStream;", params);
if(NULL == jStream)
{
lprintf("NULL object from AiIOSystem.open\n");
return NULL;
}
size_t size = calli(mJniEnv, jStream, "jassimp/AiIOStream", "getFileSize", "()I");
lprintf("Model file size is %d\n", size);
char* buffer = (char*)malloc(size);
jobject javaBuffer = mJniEnv->NewDirectByteBuffer(buffer, size);
jvalue readParams[1];
readParams[0].l = javaBuffer;
if(call(mJniEnv, jStream, "jassimp/AiIOStream", "read", "(Ljava/nio/ByteBuffer;)Z", readParams))
{
return new JavaIOStream(size, buffer, jStream);
}
else
{
lprintf("Read failure on AiIOStream.read");
free(buffer);
return NULL;
}
};
void Close( Assimp::IOStream* pFile)
{
jvalue params[1];
params[0].l = ((JavaIOStream*) pFile)->javaObject();
callv(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "close", "(Ljassimp/AiIOStream;)V", params);
delete pFile;
};
};
static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene)
@ -1474,7 +1705,7 @@ JNIEXPORT jint JNICALL Java_jassimp_Jassimp_getlongsize
JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString
(JNIEnv *env, jclass jClazz)
{
const char *err = aiGetErrorString();
const char *err = gLastErrorString.c_str();
if (NULL == err)
{
@ -1486,18 +1717,26 @@ JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString
JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile
(JNIEnv *env, jclass jClazz, jstring jFilename, jlong postProcess)
(JNIEnv *env, jclass jClazz, jstring jFilename, jlong postProcess, jobject ioSystem)
{
jobject jScene = NULL;
/* convert params */
const char* cFilename = env->GetStringUTFChars(jFilename, NULL);
Assimp::Importer imp;
if(ioSystem != NULL)
{
imp.SetIOHandler(new JavaIOSystem(env, ioSystem));
lprintf("Created aiFileIO\n");
}
lprintf("opening file: %s\n", cFilename);
/* do import */
const aiScene *cScene = aiImportFile(cFilename, (unsigned int) postProcess);
const aiScene *cScene = imp.ReadFile(cFilename, (unsigned int) postProcess);
if (!cScene)
{
@ -1552,19 +1791,13 @@ error:
/* thats really a problem because we cannot throw in this case */
env->FatalError("could not throw java.io.IOException");
}
env->ThrowNew(exception, aiGetErrorString());
gLastErrorString = imp.GetErrorString();
env->ThrowNew(exception, gLastErrorString.c_str());
lprintf("problem detected\n");
}
end:
/*
* NOTE: this releases all memory used in the native domain.
* Ensure all data has been passed to java before!
*/
aiReleaseImport(cScene);
/* free params */
env->ReleaseStringUTFChars(jFilename, cFilename);

View File

@ -39,7 +39,7 @@ JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString
* Signature: (Ljava/lang/String;J)Ljassimp/AiScene;
*/
JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile
(JNIEnv *, jclass, jstring, jlong);
(JNIEnv *, jclass, jstring, jlong, jobject);
#ifdef __cplusplus
}

View File

@ -0,0 +1,130 @@
/*
* Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jassimp;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
* IOSystem based on the Java classloader.<p>
*
* This IOSystem allows loading models directly from the
* classpath. No extraction to the file system is
* necessary.
*
* @author Jesper Smith
*
*/
public class AiClassLoaderIOSystem implements AiIOSystem<AiInputStreamIOStream>
{
private final Class<?> clazz;
private final ClassLoader classLoader;
/**
* Construct a new AiClassLoaderIOSystem.<p>
*
* This constructor uses a ClassLoader to resolve
* resources.
*
* @param classLoader classLoader to resolve resources.
*/
public AiClassLoaderIOSystem(ClassLoader classLoader) {
this.clazz = null;
this.classLoader = classLoader;
}
/**
* Construct a new AiClassLoaderIOSystem.<p>
*
* This constructor uses a Class to resolve
* resources.
*
* @param class<?> class to resolve resources.
*/
public AiClassLoaderIOSystem(Class<?> clazz) {
this.clazz = clazz;
this.classLoader = null;
}
@Override
public AiInputStreamIOStream open(String filename, String ioMode) {
try {
InputStream is;
if(clazz != null) {
is = clazz.getResourceAsStream(filename);
}
else if (classLoader != null) {
is = classLoader.getResourceAsStream(filename);
}
else {
System.err.println("[" + getClass().getSimpleName() +
"] No class or classLoader provided to resolve " + filename);
return null;
}
if(is != null) {
return new AiInputStreamIOStream(is);
}
else {
System.err.println("[" + getClass().getSimpleName() +
"] Cannot find " + filename);
return null;
}
}
catch (IOException e) {
e.printStackTrace();
return null;
}
}
@Override
public void close(AiInputStreamIOStream file) {
}
@Override
public boolean exists(String path)
{
URL url = null;
if(clazz != null) {
url = clazz.getResource(path);
}
else if (classLoader != null) {
url = classLoader.getResource(path);
}
if(url == null)
{
return false;
}
else
{
return true;
}
}
@Override
public char getOsSeparator()
{
return '/';
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jassimp;
import java.nio.ByteBuffer;
/**
* Interface to allow custom resource loaders for jassimp.<p>
*
* The design is based on passing the file wholly in memory,
* because Java inputstreams do not have to support seek. <p>
*
* Writing files from Java is unsupported.
*
*
* @author Jesper Smith
*
*/
public interface AiIOStream
{
/**
* Read all data into buffer. <p>
*
* The whole stream should be read into the buffer.
* No support is provided for partial reads.
*
* @param buffer Target buffer for the model data
*
* @return true if successful, false if an error occurred.
*/
boolean read(ByteBuffer buffer);
/**
* The total size of this stream. <p>
*
* @return total size of this stream
*/
int getFileSize();
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jassimp;
public interface AiIOSystem <T extends AiIOStream>
{
/**
*
* Open a new file with a given path.
* When the access to the file is finished, call close() to release all associated resources
*
* @param path Path to the file
* @param ioMode file I/O mode. Required are: "wb", "w", "wt", "rb", "r", "rt".
*
* @return AiIOStream or null if an error occurred
*/
public T open(String path, String ioMode);
/**
* Tests for the existence of a file at the given path.
*
* @param path path to the file
* @return true if there is a file with this path, else false.
*/
public boolean exists(String path);
/**
* Returns the system specific directory separator.<p>
*
* @return System specific directory separator
*/
public char getOsSeparator();
/**
* Closes the given file and releases all resources associated with it.
*
* @param file The file instance previously created by Open().
*/
public void close(T file);
}

View File

@ -0,0 +1,102 @@
/*
* Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jassimp;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.nio.ByteBuffer;
/**
* Implementation of AiIOStream reading from a InputStream
*
* @author Jesper Smith
*
*/
public class AiInputStreamIOStream implements AiIOStream
{
private final ByteArrayOutputStream os = new ByteArrayOutputStream();
public AiInputStreamIOStream(URI uri) throws IOException {
this(uri.toURL());
}
public AiInputStreamIOStream(URL url) throws IOException {
this(url.openStream());
}
public AiInputStreamIOStream(InputStream is) throws IOException {
int read;
byte[] data = new byte[1024];
while((read = is.read(data, 0, data.length)) != -1) {
os.write(data, 0, read);
}
os.flush();
is.close();
}
@Override
public int getFileSize() {
return os.size();
}
@Override
public boolean read(ByteBuffer buffer) {
ByteBufferOutputStream bos = new ByteBufferOutputStream(buffer);
try
{
os.writeTo(bos);
}
catch (IOException e)
{
e.printStackTrace();
return false;
}
return true;
}
/**
* Internal helper class to copy the contents of an OutputStream
* into a ByteBuffer. This avoids a copy.
*
*/
private static class ByteBufferOutputStream extends OutputStream {
private final ByteBuffer buffer;
public ByteBufferOutputStream(ByteBuffer buffer) {
this.buffer = buffer;
}
@Override
public void write(int b) throws IOException
{
buffer.put((byte) b);
}
@Override
public void write(byte b[], int off, int len) throws IOException {
buffer.put(b, off, len);
}
}
}

View File

@ -79,6 +79,20 @@ public final class Jassimp {
return importFile(filename, EnumSet.noneOf(AiPostProcessSteps.class));
}
/**
* Imports a file via assimp without post processing.
*
* @param filename the file to import
* @param ioSystem ioSystem to load files, or null for default
* @return the loaded scene
* @throws IOException if an error occurs
*/
public static AiScene importFile(String filename, AiIOSystem<?> ioSystem)
throws IOException {
return importFile(filename, EnumSet.noneOf(AiPostProcessSteps.class), ioSystem);
}
/**
* Imports a file via assimp.
@ -89,12 +103,28 @@ public final class Jassimp {
* @throws IOException if an error occurs
*/
public static AiScene importFile(String filename,
Set<AiPostProcessSteps> postProcessing) throws IOException {
Set<AiPostProcessSteps> postProcessing)
throws IOException {
return importFile(filename, postProcessing, null);
}
/**
* Imports a file via assimp.
*
* @param filename the file to import
* @param postProcessing post processing flags
* @param ioSystem ioSystem to load files, or null for default
* @return the loaded scene, or null if an error occurred
* @throws IOException if an error occurs
*/
public static AiScene importFile(String filename,
Set<AiPostProcessSteps> postProcessing, AiIOSystem<?> ioSystem)
throws IOException {
loadLibrary();
return aiImportFile(filename, AiPostProcessSteps.toRawValue(
postProcessing));
postProcessing), ioSystem);
}
@ -310,7 +340,7 @@ public final class Jassimp {
* @throws IOException if an error occurs
*/
private static native AiScene aiImportFile(String filename,
long postProcessing) throws IOException;
long postProcessing, AiIOSystem<?> ioSystem) throws IOException;
/**

View File

@ -90,28 +90,36 @@ void PretransformVerticesTest::SetUp()
// add 5 empty materials
scene->mMaterials = new aiMaterial*[scene->mNumMaterials = 5];
for (unsigned int i = 0; i < 5;++i)
for (unsigned int i = 0; i < 5;++i) {
scene->mMaterials[i] = new aiMaterial();
}
// add 25 test meshes
scene->mMeshes = new aiMesh*[scene->mNumMeshes = 25];
for (unsigned int i = 0; i < 25;++i) {
aiMesh* mesh = scene->mMeshes[i] = new aiMesh();
for ( unsigned int i = 0; i < 25; ++i) {
aiMesh* mesh = scene->mMeshes[ i ] = new aiMesh();
mesh->mPrimitiveTypes = aiPrimitiveType_POINT;
mesh->mFaces = new aiFace[ mesh->mNumFaces = 10+i ];
mesh->mVertices = new aiVector3D[mesh->mNumVertices = mesh->mNumFaces];
for (unsigned int a = 0; a < mesh->mNumFaces; ++a ) {
aiFace& f = mesh->mFaces[a];
f.mIndices = new unsigned int [f.mNumIndices = 1];
aiFace& f = mesh->mFaces[ a ];
f.mIndices = new unsigned int [ f.mNumIndices = 1 ];
f.mIndices[0] = a*3;
mesh->mVertices[a] = aiVector3D((float)i,(float)a,0.f);
}
mesh->mMaterialIndex = i%5;
if (i % 2)
if (i % 2) {
mesh->mNormals = new aiVector3D[mesh->mNumVertices];
for ( unsigned int normalIdx=0; normalIdx<mesh->mNumVertices; ++normalIdx ) {
mesh->mNormals[ normalIdx ].x = 1.0f;
mesh->mNormals[ normalIdx ].y = 1.0f;
mesh->mNormals[ normalIdx ].z = 1.0f;
mesh->mNormals[ normalIdx ].Normalize();
}
}
}
// construct some nodes (1+25)