summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKenneth Russel <[email protected]>2006-01-03 02:36:17 +0000
committerKenneth Russel <[email protected]>2006-01-03 02:36:17 +0000
commitb69f8b201ba3767020d33a7ebe066466f00d4223 (patch)
treeac4c77e5256026f4c1de771e49d89941645e60e2
parent6b4308e8b3dc41d588ff9e7519da813705a3cd94 (diff)
Added checks on number of remaining elements and bytes for Buffers and
arrays passed to certain APIs. Default is to not emit such range checks. The checks are currently most fully implemented for image- and texture-related APIs. Verified with debugging code and with demos that all textures used in demos are properly checked. git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/trunk@509 232f8b59-042b-4e1e-8c03-345bb8c30851
-rw-r--r--make/gl-common.cfg45
-rw-r--r--make/gl-impl-CustomJavaCode.java96
-rw-r--r--src/classes/com/sun/gluegen/JavaConfiguration.java53
-rw-r--r--src/classes/com/sun/gluegen/JavaEmitter.java6
-rw-r--r--src/classes/com/sun/gluegen/JavaMethodBindingEmitter.java68
-rw-r--r--src/classes/com/sun/gluegen/runtime/BufferFactory.java77
6 files changed, 339 insertions, 6 deletions
diff --git a/make/gl-common.cfg b/make/gl-common.cfg
index ab8af9e09..dfe01cc05 100644
--- a/make/gl-common.cfg
+++ b/make/gl-common.cfg
@@ -331,6 +331,51 @@ BufferObjectKind Element glDrawElements
BufferObjectKind Element glDrawRangeElements
BufferObjectKind Element glDrawRangeElementsEXT
+# Range check directives for various routines
+# FIXME: some of these are really the bare minimum and won't catch
+# many classes of errors. Should extend the DebugGL to perform much
+# more error checking with e.g. glDrawElements.
+RangeCheck glColorPointer 3 1
+RangeCheck glDrawElements 3 {1}
+RangeCheck glDrawRangeElements 5 {3}
+RangeCheck glEdgeFlagPointer 1 1
+RangeCheck glElementPointerATI 1 1
+RangeCheck glFogCoordPointer 2 1
+RangeCheck glFogCoordPointerEXT 2 1
+RangeCheck glInterleavedArrays 2 1
+RangeCheck glMatrixIndexPointerARB 3 1
+RangeCheck glNormalPointer 2 1
+RangeCheck glSecondaryColorPointer 3 1
+RangeCheck glSecondaryColorPointerEXT 3 1
+RangeCheck glTexCoordPointer 3 1
+RangeCheck glVariantPointerEXT 3 1
+RangeCheck glVertexPointer 3 1
+RangeCheck glVertexAttribPointer 5 1
+RangeCheck glVertexAttribPointerARB 5 1
+RangeCheck glWeightPointerARB 3 1
+
+# Range check directives for various image-related routines
+RangeCheckBytes glColorTable 5 imageSizeInBytes({3}, {4}, {2} , 1 , 1)
+RangeCheckBytes glColorTableEXT 5 imageSizeInBytes({3}, {4}, {2} , 1 , 1)
+RangeCheckBytes glConvolutionFilter1D 5 imageSizeInBytes({3}, {4}, {2} , 1 , 1)
+RangeCheckBytes glConvolutionFilter2D 6 imageSizeInBytes({4}, {5}, {2} , {3} , 1)
+RangeCheckBytes glDrawPixels 4 imageSizeInBytes({2}, {3}, {0} , {1} , 1)
+RangeCheckBytes glReadPixels 6 imageSizeInBytes({4}, {5}, {2} , {3} , 1)
+RangeCheckBytes glTexImage1D 7 imageSizeInBytes({5}, {6}, {3} + (2 * {4}), 1 , 1)
+RangeCheckBytes glTexImage2D 8 imageSizeInBytes({6}, {7}, {3} + (2 * {5}), {4} + (2 * {5}), 1)
+RangeCheckBytes glTexImage3D 9 imageSizeInBytes({7}, {8}, {3} + (2 * {6}), {4} + (2 * {6}), {5} + (2 * {6}))
+RangeCheckBytes glTexSubImage1D 6 imageSizeInBytes({4}, {5}, {3} , 1 , 1)
+RangeCheckBytes glTexSubImage2D 8 imageSizeInBytes({6}, {7}, {4} , {5} , 1)
+RangeCheckBytes glTexSubImage3D 10 imageSizeInBytes({8}, {9}, {5} , {6} , {7})
+# Note we don't support glTexImage4DSGIS / glTexSubImage4DSGIS
+
+# Not simple to produce good range checks for these as we would need
+# to query the pipeline to see the size of the returned data before
+# fetching it
+# RangeCheckBytes glGetTexImage
+
+
+
# Javadoc for the GL class
ClassJavadoc GL /**
ClassJavadoc GL * <P> The basic interface to OpenGL, providing access to core
diff --git a/make/gl-impl-CustomJavaCode.java b/make/gl-impl-CustomJavaCode.java
index 3d927d271..7f4902c83 100644
--- a/make/gl-impl-CustomJavaCode.java
+++ b/make/gl-impl-CustomJavaCode.java
@@ -37,6 +37,102 @@ public Object getPlatformGLExtensions() {
return _context.getPlatformGLExtensions();
}
+//
+// Helpers for ensuring the correct amount of texture data
+//
+
+/** Returns the number of bytes required to fill in the appropriate
+ texture. This is regrettably a lower bound as in certain
+ circumstances OpenGL state such as unpack alignment can cause more
+ data to be required. However this should be close enough that it
+ should catch most crashes. The logic in this routine is based on
+ code in the SGI OpenGL sample implementation. */
+
+private int imageSizeInBytes(int format, int type, int w, int h, int d) {
+ int elements = 0;
+ int esize = 0;
+
+ if (w < 0) return 0;
+ if (h < 0) return 0;
+ if (d < 0) return 0;
+ switch (format) {
+ case GL_COLOR_INDEX:
+ case GL_STENCIL_INDEX:
+ elements = 1;
+ break;
+ case GL_RED:
+ case GL_GREEN:
+ case GL_BLUE:
+ case GL_ALPHA:
+ case GL_LUMINANCE:
+ case GL_DEPTH_COMPONENT:
+ elements = 1;
+ break;
+ case GL_LUMINANCE_ALPHA:
+ elements = 2;
+ break;
+ case GL_RGB:
+ case GL_BGR:
+ elements = 3;
+ break;
+ case GL_RGBA:
+ case GL_BGRA:
+ case GL_ABGR_EXT:
+ elements = 4;
+ break;
+ case GL_HILO_NV:
+ elements = 2;
+ break;
+ default:
+ return 0;
+ }
+ switch (type) {
+ case GL_BITMAP:
+ if (format == GL_COLOR_INDEX) {
+ return (d * (h * ((w+7)/8)));
+ } else {
+ return 0;
+ }
+ case GL_BYTE:
+ case GL_UNSIGNED_BYTE:
+ esize = 1;
+ break;
+ case GL_UNSIGNED_BYTE_3_3_2:
+ case GL_UNSIGNED_BYTE_2_3_3_REV:
+ esize = 1;
+ elements = 1;
+ break;
+ case GL_SHORT:
+ case GL_UNSIGNED_SHORT:
+ esize = 2;
+ break;
+ case GL_UNSIGNED_SHORT_5_6_5:
+ case GL_UNSIGNED_SHORT_5_6_5_REV:
+ case GL_UNSIGNED_SHORT_4_4_4_4:
+ case GL_UNSIGNED_SHORT_4_4_4_4_REV:
+ case GL_UNSIGNED_SHORT_5_5_5_1:
+ case GL_UNSIGNED_SHORT_1_5_5_5_REV:
+ esize = 2;
+ elements = 1;
+ break;
+ case GL_INT:
+ case GL_UNSIGNED_INT:
+ case GL_FLOAT:
+ esize = 4;
+ break;
+ case GL_UNSIGNED_INT_8_8_8_8:
+ case GL_UNSIGNED_INT_8_8_8_8_REV:
+ case GL_UNSIGNED_INT_10_10_10_2:
+ case GL_UNSIGNED_INT_2_10_10_10_REV:
+ esize = 4;
+ elements = 1;
+ break;
+ default:
+ return 0;
+ }
+ return (elements * esize * w * h * d);
+}
+
private int[] bufTmp = new int[1];
private void checkBufferObject(String extension1,
String extension2,
diff --git a/src/classes/com/sun/gluegen/JavaConfiguration.java b/src/classes/com/sun/gluegen/JavaConfiguration.java
index 152d9cf80..ae8c46eaa 100644
--- a/src/classes/com/sun/gluegen/JavaConfiguration.java
+++ b/src/classes/com/sun/gluegen/JavaConfiguration.java
@@ -116,6 +116,8 @@ public class JavaConfiguration {
private Map/*<String,String>*/ javaMethodRenames = new HashMap();
private Map/*<String,List<String>>*/ javaPrologues = new HashMap();
private Map/*<String,List<String>>*/ javaEpilogues = new HashMap();
+ private Map/*<String,Map<Integer,String>>*/ rangeChecks = new HashMap();
+ private Map/*<String,Map<Integer,String>>*/ byteRangeChecks = new HashMap();
/** Reads the configuration file.
@param filename path to file that should be read
@@ -569,6 +571,22 @@ public class JavaConfiguration {
return res;
}
+ /** Returns a map of Integer argument numbers to expressions to be
+ used to range check the given arguments to the given function.
+ Returns null if there were no range check expressions supplied
+ for this function. */
+ public Map/*<Integer, String>*/ rangeCheckExpressions(String functionName) {
+ return (Map/*<Integer, String>*/) rangeChecks.get(functionName);
+ }
+
+ /** Returns a map of Integer argument numbers to expressions to be
+ used to range check (in size of bytes) the given arguments to
+ the given function. Returns null if there were no range check
+ expressions supplied for this function. */
+ public Map/*<Integer, String>*/ byteRangeCheckExpressions(String functionName) {
+ return (Map/*<Integer, String>*/) byteRangeChecks.get(functionName);
+ }
+
//----------------------------------------------------------------------
// Internals only below this point
//
@@ -686,6 +704,14 @@ public class JavaConfiguration {
readJavaPrologueOrEpilogue(tok, filename, lineNo, false);
// Warning: make sure delimiters are reset at the top of this loop
// because readJavaPrologueOrEpilogue changes them.
+ } else if (cmd.equalsIgnoreCase("RangeCheck")) {
+ readRangeCheck(tok, filename, lineNo, false);
+ // Warning: make sure delimiters are reset at the top of this loop
+ // because RangeCheck changes them.
+ } else if (cmd.equalsIgnoreCase("RangeCheckBytes")) {
+ readRangeCheck(tok, filename, lineNo, true);
+ // Warning: make sure delimiters are reset at the top of this loop
+ // because RangeCheckBytes changes them.
} else {
throw new RuntimeException("Unknown command \"" + cmd +
"\" in command file " + filename +
@@ -1131,6 +1157,33 @@ public class JavaConfiguration {
}
}
+ protected void readRangeCheck(StringTokenizer tok, String filename, int lineNo, boolean inBytes) {
+ try {
+ String functionName = tok.nextToken();
+ int argNum = Integer.parseInt(tok.nextToken());
+ String restOfLine = tok.nextToken("\n\r\f");
+ restOfLine = restOfLine.trim();
+ Map/*<Integer, String>*/ checksForFunction = null;
+ if (inBytes) {
+ checksForFunction = (Map/*<Integer, String>*/) byteRangeChecks.get(functionName);
+ } else {
+ checksForFunction = (Map/*<Integer, String>*/) rangeChecks.get(functionName);
+ }
+ if (checksForFunction == null) {
+ checksForFunction = new HashMap/*<Integer, String>*/();
+ if (inBytes) {
+ byteRangeChecks.put(functionName, checksForFunction);
+ } else {
+ rangeChecks.put(functionName, checksForFunction);
+ }
+ }
+ checksForFunction.put(new Integer(argNum), restOfLine);
+ } catch (Exception e) {
+ throw new RuntimeException("Error parsing \"RangeCheck" + (inBytes ? "Bytes" : "") + "\" command at line " + lineNo +
+ " in file \"" + filename + "\"", e);
+ }
+ }
+
protected static TypeInfo parseTypeInfo(String cType, JavaType javaType) {
String typeName = null;
int pointerDepth = 0;
diff --git a/src/classes/com/sun/gluegen/JavaEmitter.java b/src/classes/com/sun/gluegen/JavaEmitter.java
index d4bafb7a8..f0799067e 100644
--- a/src/classes/com/sun/gluegen/JavaEmitter.java
+++ b/src/classes/com/sun/gluegen/JavaEmitter.java
@@ -377,6 +377,8 @@ public class JavaEmitter implements GlueEmitter {
emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName()));
emitter.setPrologue(prologue);
emitter.setEpilogue(epilogue);
+ emitter.setRangeCheckExpressions(cfg.rangeCheckExpressions(binding.getName()));
+ emitter.setByteRangeCheckExpressions(cfg.byteRangeCheckExpressions(binding.getName()));
allEmitters.add(emitter);
}
@@ -430,6 +432,8 @@ public class JavaEmitter implements GlueEmitter {
}
emitter.addModifier(JavaMethodBindingEmitter.NATIVE);
emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName()));
+ emitter.setRangeCheckExpressions(cfg.rangeCheckExpressions(binding.getName()));
+ emitter.setByteRangeCheckExpressions(cfg.byteRangeCheckExpressions(binding.getName()));
allEmitters.add(emitter);
// Optionally emit the entry point taking arrays which handles
@@ -455,6 +459,8 @@ public class JavaEmitter implements GlueEmitter {
}
emitter.addModifier(JavaMethodBindingEmitter.NATIVE);
emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName()));
+ emitter.setRangeCheckExpressions(cfg.rangeCheckExpressions(binding.getName()));
+ emitter.setByteRangeCheckExpressions(cfg.byteRangeCheckExpressions(binding.getName()));
allEmitters.add(emitter);
}
}
diff --git a/src/classes/com/sun/gluegen/JavaMethodBindingEmitter.java b/src/classes/com/sun/gluegen/JavaMethodBindingEmitter.java
index 5a7e065f7..7e08977a6 100644
--- a/src/classes/com/sun/gluegen/JavaMethodBindingEmitter.java
+++ b/src/classes/com/sun/gluegen/JavaMethodBindingEmitter.java
@@ -87,6 +87,10 @@ public class JavaMethodBindingEmitter extends FunctionEmitter
// number of elements of the returned array.
private String returnedArrayLengthExpression;
+ // Range-check expressions for various Buffer arguments
+ private Map/*<Integer, String>*/ rangeCheckExpressions;
+ private Map/*<Integer, String>*/ byteRangeCheckExpressions;
+
public JavaMethodBindingEmitter(MethodBinding binding,
PrintWriter output,
String runtimeExceptionType,
@@ -126,9 +130,11 @@ public class JavaMethodBindingEmitter extends FunctionEmitter
forDirectBufferImplementation = arg.forDirectBufferImplementation;
forIndirectBufferAndArrayImplementation = arg.forIndirectBufferAndArrayImplementation;
isUnimplemented = arg.isUnimplemented;
- returnedArrayLengthExpression = arg.returnedArrayLengthExpression;
prologue = arg.prologue;
epilogue = arg.epilogue;
+ returnedArrayLengthExpression = arg.returnedArrayLengthExpression;
+ rangeCheckExpressions = arg.rangeCheckExpressions;
+ byteRangeCheckExpressions = arg.byteRangeCheckExpressions;
}
public final MethodBinding getBinding() { return binding; }
@@ -193,6 +199,14 @@ public class JavaMethodBindingEmitter extends FunctionEmitter
this.forImplementingMethodCall = impl;
}
+ public void setRangeCheckExpressions(Map/*<Integer, String>*/ rangeChecks) {
+ this.rangeCheckExpressions = rangeChecks;
+ }
+
+ public void setByteRangeCheckExpressions(Map/*<Integer, String>*/ rangeChecks) {
+ this.byteRangeCheckExpressions = rangeChecks;
+ }
+
protected void emitReturnType(PrintWriter writer)
{
writer.print(getReturnTypeString(false));
@@ -424,6 +438,44 @@ public class JavaMethodBindingEmitter extends FunctionEmitter
}
}
}
+
+ emitManuallySpecifiedRangeChecks(rangeCheckExpressions, false, writer);
+ emitManuallySpecifiedRangeChecks(byteRangeCheckExpressions, true, writer);
+ }
+
+ protected void emitManuallySpecifiedRangeChecks(Map/*<Integer, String>*/ rangeChecks,
+ boolean inBytes,
+ PrintWriter writer) {
+ if (rangeChecks == null) {
+ return;
+ }
+
+ // Check lengths of arrays and buffers with user-specified checks
+ for (Iterator iter = rangeChecks.keySet().iterator(); iter.hasNext(); ) {
+ Integer argNumBox = (Integer) iter.next();
+ int argNum = argNumBox.intValue();
+ JavaType type = binding.getJavaArgumentType(argNum);
+ String argName = getArgumentName(argNum);
+ if (type.isPrimitiveArray()) {
+ String offsetArg = offsetArgName(argNum);
+ if (inBytes) {
+ throw new RuntimeException("Can not specify RangeCheckBytes for primitive array arguments (failed on function " +
+ binding.getName() + ", argument " + argName + ")");
+ }
+ writer.println(" BufferFactory.rangeCheck(" + argName + ", " + offsetArg + ", " +
+ new MessageFormat((String) rangeChecks.get(argNumBox)).format(argumentNameArray()) +
+ ");");
+ } else if (!type.isPrimitive()) {
+ // Assume it's a Buffer
+ writer.print(" BufferFactory.rangeCheck");
+ if (inBytes) {
+ writer.print("Bytes");
+ }
+ writer.println("(" + argName + ", " +
+ new MessageFormat((String) rangeChecks.get(argNumBox)).format(argumentNameArray()) +
+ ");");
+ }
+ }
}
protected void emitCall(MethodBinding binding, PrintWriter writer, boolean direct) {
@@ -601,11 +653,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter
writer.print(" return " + returnType.getName() + ".create(_res.order(ByteOrder.nativeOrder()))");
} else {
writer.println(" _res.order(ByteOrder.nativeOrder());");
- String[] argumentNames = new String[binding.getNumArguments()];
- for (int i = 0; i < binding.getNumArguments(); i++) {
- argumentNames[i] = getArgumentName(i);
- }
- String expr = new MessageFormat(fmt).format(argumentNames);
+ String expr = new MessageFormat(fmt).format(argumentNameArray());
PointerType cReturnTypePointer = binding.getCReturnType().asPointer();
CompoundType cReturnType = null;
if (cReturnTypePointer != null) {
@@ -648,6 +696,14 @@ public class JavaMethodBindingEmitter extends FunctionEmitter
}
}
+ protected String[] argumentNameArray() {
+ String[] argumentNames = new String[binding.getNumArguments()];
+ for (int i = 0; i < binding.getNumArguments(); i++) {
+ argumentNames[i] = getArgumentName(i);
+ }
+ return argumentNames;
+ }
+
public static String javaThisArgumentName() {
return "jthis0";
}
diff --git a/src/classes/com/sun/gluegen/runtime/BufferFactory.java b/src/classes/com/sun/gluegen/runtime/BufferFactory.java
index ca66e6915..dd8856a43 100644
--- a/src/classes/com/sun/gluegen/runtime/BufferFactory.java
+++ b/src/classes/com/sun/gluegen/runtime/BufferFactory.java
@@ -161,4 +161,81 @@ public class BufferFactory {
throw new RuntimeException("Unknown buffer type " + buf.getClass().getName());
}
+
+ public static void rangeCheck(byte[] array, int offset, int minElementsRemaining) {
+ if (array.length < offset + minElementsRemaining) {
+ throw new ArrayIndexOutOfBoundsException("Required " + minElementsRemaining + " elements in array, only had " + (array.length - offset));
+ }
+ }
+
+ public static void rangeCheck(char[] array, int offset, int minElementsRemaining) {
+ if (array.length < offset + minElementsRemaining) {
+ throw new ArrayIndexOutOfBoundsException("Required " + minElementsRemaining + " elements in array, only had " + (array.length - offset));
+ }
+ }
+
+ public static void rangeCheck(short[] array, int offset, int minElementsRemaining) {
+ if (array.length < offset + minElementsRemaining) {
+ throw new ArrayIndexOutOfBoundsException("Required " + minElementsRemaining + " elements in array, only had " + (array.length - offset));
+ }
+ }
+
+ public static void rangeCheck(int[] array, int offset, int minElementsRemaining) {
+ if (array.length < offset + minElementsRemaining) {
+ throw new ArrayIndexOutOfBoundsException("Required " + minElementsRemaining + " elements in array, only had " + (array.length - offset));
+ }
+ }
+
+ public static void rangeCheck(long[] array, int offset, int minElementsRemaining) {
+ if (array.length < offset + minElementsRemaining) {
+ throw new ArrayIndexOutOfBoundsException("Required " + minElementsRemaining + " elements in array, only had " + (array.length - offset));
+ }
+ }
+
+ public static void rangeCheck(float[] array, int offset, int minElementsRemaining) {
+ if (array.length < offset + minElementsRemaining) {
+ throw new ArrayIndexOutOfBoundsException("Required " + minElementsRemaining + " elements in array, only had " + (array.length - offset));
+ }
+ }
+
+ public static void rangeCheck(double[] array, int offset, int minElementsRemaining) {
+ if (array.length < offset + minElementsRemaining) {
+ throw new ArrayIndexOutOfBoundsException("Required " + minElementsRemaining + " elements in array, only had " + (array.length - offset));
+ }
+ }
+
+ public static void rangeCheck(Buffer buffer, int minElementsRemaining) {
+ if (buffer == null) {
+ return;
+ }
+
+ if (buffer.remaining() < minElementsRemaining) {
+ throw new IndexOutOfBoundsException("Required " + minElementsRemaining + " remaining elements in buffer, only had " + buffer.remaining());
+ }
+ }
+
+ public static void rangeCheckBytes(Buffer buffer, int minBytesRemaining) {
+ if (buffer == null) {
+ return;
+ }
+
+ int elementsRemaining = buffer.remaining();
+ int bytesRemaining = 0;
+ if (buffer instanceof ByteBuffer) {
+ bytesRemaining = elementsRemaining;
+ } else if (buffer instanceof FloatBuffer) {
+ bytesRemaining = elementsRemaining * SIZEOF_FLOAT;
+ } else if (buffer instanceof IntBuffer) {
+ bytesRemaining = elementsRemaining * SIZEOF_INT;
+ } else if (buffer instanceof ShortBuffer) {
+ bytesRemaining = elementsRemaining * SIZEOF_SHORT;
+ } else if (buffer instanceof DoubleBuffer) {
+ bytesRemaining = elementsRemaining * SIZEOF_DOUBLE;
+ } else if (buffer instanceof LongBuffer) {
+ bytesRemaining = elementsRemaining * SIZEOF_LONG;
+ }
+ if (bytesRemaining < minBytesRemaining) {
+ throw new IndexOutOfBoundsException("Required " + minBytesRemaining + " remaining bytes in buffer, only had " + bytesRemaining);
+ }
+ }
}