提交 afc66428 编写于 作者: P prr

6632886: Font.createFont can be persuaded to leak temporary files

6522586: Enforce limits on Font creation
6652929: Font.createFont(int,File) trusts File.getPath
Reviewed-by: igor
上级 2e6ce471
......@@ -37,6 +37,8 @@ import java.awt.geom.Rectangle2D;
import java.awt.peer.FontPeer;
import java.io.*;
import java.lang.ref.SoftReference;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.text.AttributedCharacterIterator.Attribute;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
......@@ -51,6 +53,7 @@ import sun.font.AttributeMap;
import sun.font.AttributeValues;
import sun.font.EAttribute;
import sun.font.CompositeFont;
import sun.font.CreatedFontTracker;
import sun.font.Font2D;
import sun.font.Font2DHandle;
import sun.font.FontManager;
......@@ -575,14 +578,16 @@ public class Font implements java.io.Serializable
}
/* used to implement Font.createFont */
private Font(File fontFile, int fontFormat, boolean isCopy)
private Font(File fontFile, int fontFormat,
boolean isCopy, CreatedFontTracker tracker)
throws FontFormatException {
this.createdFont = true;
/* Font2D instances created by this method track their font file
* so that when the Font2D is GC'd it can also remove the file.
*/
this.font2DHandle =
FontManager.createFont2D(fontFile, fontFormat, isCopy).handle;
FontManager.createFont2D(fontFile, fontFormat,
isCopy, tracker).handle;
this.name = this.font2DHandle.font2D.getFontName(Locale.getDefault());
this.style = Font.PLAIN;
this.size = 1;
......@@ -787,6 +792,29 @@ public class Font implements java.io.Serializable
return new Font(attributes);
}
/**
* Used with the byte count tracker for fonts created from streams.
* If a thread can create temp files anyway, no point in counting
* font bytes.
*/
private static boolean hasTempPermission() {
if (System.getSecurityManager() == null) {
return true;
}
File f = null;
boolean hasPerm = false;
try {
f = File.createTempFile("+~JT", ".tmp", null);
f.delete();
f = null;
hasPerm = true;
} catch (Throwable t) {
/* inc. any kind of SecurityException */
}
return hasPerm;
}
/**
* Returns a new <code>Font</code> using the specified font type
* and input data. The new <code>Font</code> is
......@@ -822,58 +850,96 @@ public class Font implements java.io.Serializable
fontFormat != Font.TYPE1_FONT) {
throw new IllegalArgumentException ("font format not recognized");
}
final InputStream fStream = fontStream;
Object ret = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
File tFile = null;
FileOutputStream outStream = null;
try {
tFile = File.createTempFile("+~JF", ".tmp", null);
/* Temp file deleted by font shutdown hook */
BufferedInputStream inStream =
new BufferedInputStream(fStream);
outStream = new FileOutputStream(tFile);
int bytesRead = 0;
int bufSize = 8192;
byte [] buf = new byte[bufSize];
while (bytesRead != -1) {
try {
bytesRead = inStream.read(buf, 0, bufSize);
} catch (Throwable t) {
throw new IOException();
}
if (bytesRead != -1) {
outStream.write(buf, 0, bytesRead);
}
}
/* don't close the input stream */
outStream.close();
} catch (IOException e) {
if (outStream != null) {
try {
outStream.close();
} catch (Exception e1) {
}
}
if (tFile != null) {
try {
tFile.delete();
} catch (Exception e2) {
}
}
return e;
}
return tFile;
}
});
if (ret instanceof File) {
return new Font((File)ret, fontFormat, true);
} else if (ret instanceof IOException) {
throw (IOException)ret;
} else {
throw new FontFormatException("Couldn't access font stream");
boolean copiedFontData = false;
try {
final File tFile = AccessController.doPrivileged(
new PrivilegedExceptionAction<File>() {
public File run() throws IOException {
return File.createTempFile("+~JF", ".tmp", null);
}
}
);
int totalSize = 0;
CreatedFontTracker tracker = null;
try {
final OutputStream outStream =
AccessController.doPrivileged(
new PrivilegedExceptionAction<OutputStream>() {
public OutputStream run() throws IOException {
return new FileOutputStream(tFile);
}
}
);
if (!hasTempPermission()) {
tracker = CreatedFontTracker.getTracker();
}
try {
byte[] buf = new byte[8192];
for (;;) {
int bytesRead = fontStream.read(buf);
if (bytesRead < 0) {
break;
}
if (tracker != null) {
if (totalSize+bytesRead > tracker.MAX_FILE_SIZE) {
throw new IOException("File too big.");
}
if (totalSize+tracker.getNumBytes() >
tracker.MAX_TOTAL_BYTES)
{
throw new IOException("Total files too big.");
}
totalSize += bytesRead;
tracker.addBytes(bytesRead);
}
outStream.write(buf, 0, bytesRead);
}
/* don't close the input stream */
} finally {
outStream.close();
}
/* After all references to a Font2D are dropped, the file
* will be removed. To support long-lived AppContexts,
* we need to then decrement the byte count by the size
* of the file.
* If the data isn't a valid font, the implementation will
* delete the tmp file and decrement the byte count
* in the tracker object before returning from the
* constructor, so we can set 'copiedFontData' to true here
* without waiting for the results of that constructor.
*/
copiedFontData = true;
Font font = new Font(tFile, fontFormat, true, tracker);
return font;
} finally {
if (!copiedFontData) {
if (tracker != null) {
tracker.subBytes(totalSize);
}
AccessController.doPrivileged(
new PrivilegedExceptionAction<Void>() {
public Void run() {
tFile.delete();
return null;
}
}
);
}
}
} catch (Throwable t) {
if (t instanceof FontFormatException) {
throw (FontFormatException)t;
}
if (t instanceof IOException) {
throw (IOException)t;
}
Throwable cause = t.getCause();
if (cause instanceof FontFormatException) {
throw (FontFormatException)cause;
}
throw new IOException("Problem reading font data.");
}
}
......@@ -913,6 +979,9 @@ public class Font implements java.io.Serializable
*/
public static Font createFont(int fontFormat, File fontFile)
throws java.awt.FontFormatException, java.io.IOException {
fontFile = new File(fontFile.getPath());
if (fontFormat != Font.TRUETYPE_FONT &&
fontFormat != Font.TYPE1_FONT) {
throw new IllegalArgumentException ("font format not recognized");
......@@ -926,7 +995,7 @@ public class Font implements java.io.Serializable
if (!fontFile.canRead()) {
throw new IOException("Can't read " + fontFile);
}
return new Font(fontFile, fontFormat, false);
return new Font(fontFile, fontFormat, false, null);
}
/**
......
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.font;
public class CreatedFontTracker {
public static final int MAX_FILE_SIZE = 32 * 1024 * 1024;
public static final int MAX_TOTAL_BYTES = 10 * MAX_FILE_SIZE;
static int numBytes;
static CreatedFontTracker tracker;
public static synchronized CreatedFontTracker getTracker() {
if (tracker == null) {
tracker = new CreatedFontTracker();
}
return tracker;
}
public synchronized int getNumBytes() {
return numBytes;
}
public synchronized void addBytes(int sz) {
numBytes += sz;
}
public synchronized void subBytes(int sz) {
numBytes -= sz;
}
}
/*
* Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -125,9 +125,9 @@ public abstract class FileFont extends PhysicalFont {
return true;
}
void setFileToRemove(File file) {
void setFileToRemove(File file, CreatedFontTracker tracker) {
Disposer.addObjectRecord(this,
new CreatedFontFileDisposerRecord(file));
new CreatedFontFileDisposerRecord(file, tracker));
}
/* This is called when a font scaler is determined to
......@@ -246,12 +246,16 @@ public abstract class FileFont extends PhysicalFont {
return getScaler().getUnitsPerEm();
}
private static class CreatedFontFileDisposerRecord implements DisposerRecord {
private static class CreatedFontFileDisposerRecord
implements DisposerRecord {
File fontFile = null;
CreatedFontTracker tracker;
private CreatedFontFileDisposerRecord(File file) {
private CreatedFontFileDisposerRecord(File file,
CreatedFontTracker tracker) {
fontFile = file;
this.tracker = tracker;
}
public void dispose() {
......@@ -260,6 +264,9 @@ public abstract class FileFont extends PhysicalFont {
public Object run() {
if (fontFile != null) {
try {
if (tracker != null) {
tracker.subBytes((int)fontFile.length());
}
/* REMIND: is it possible that the file is
* still open? It will be closed when the
* font2D is disposed but could this code
......
/*
* Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -2347,12 +2347,14 @@ public final class FontManager {
static Vector<File> tmpFontFiles = null;
public static Font2D createFont2D(File fontFile, int fontFormat,
boolean isCopy)
boolean isCopy,
CreatedFontTracker tracker)
throws FontFormatException {
String fontFilePath = fontFile.getPath();
FileFont font2D = null;
final File fFile = fontFile;
final CreatedFontTracker _tracker = tracker;
try {
switch (fontFormat) {
case Font.TRUETYPE_FONT:
......@@ -2369,6 +2371,9 @@ public final class FontManager {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
if (_tracker != null) {
_tracker.subBytes((int)fFile.length());
}
fFile.delete();
return null;
}
......@@ -2377,7 +2382,7 @@ public final class FontManager {
throw(e);
}
if (isCopy) {
font2D.setFileToRemove(fontFile);
font2D.setFileToRemove(fontFile, tracker);
synchronized (FontManager.class) {
if (tmpFontFiles == null) {
......
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
import java.applet.*;
import java.awt.*;
import java.io.*;
import java.net.*;
public class BigFont extends Applet {
static private class SizedInputStream extends InputStream {
int size;
int cnt = 0;
SizedInputStream(int size) {
this.size = size;
}
public int read() {
if (cnt < size) {
cnt++;
return 0;
} else {
return -1;
}
}
public int getCurrentSize() {
return cnt;
}
}
String id;
String fileName;
public void init() {
id = getParameter("number");
fileName = getParameter("font");
System.out.println("Applet " + id + " "+
Thread.currentThread().getThreadGroup());
// Larger than size for a single font.
int fontSize = 64 * 1000 * 1000;
SizedInputStream sis = new SizedInputStream(fontSize);
try {
Font font = Font.createFont(Font.TRUETYPE_FONT, sis);
} catch (Throwable t) {
if (t instanceof FontFormatException ||
fontSize <= sis.getCurrentSize())
{
System.out.println(sis.getCurrentSize());
System.out.println(t);
throw new RuntimeException("Allowed file to be too large.");
}
}
// The following part of the test was verified manually but
// is impractical to enable because it requires a fairly large
// valid font to be part of the test, and we can't easily include
// that, nor dependably reference one from the applet environment.
/*
if (fileName == null) {
return;
}
int size = getFileSize(fileName);
if (size == 0) {
return;
}
int fontCnt = 1000 * 1000 * 1000 / size;
loadMany(size, fontCnt, fileName);
System.gc(); System.gc();
fontCnt = fontCnt / 2;
System.out.println("Applet " + id + " load more.");
loadMany(size, fontCnt, fileName);
*/
System.out.println("Applet " + id + " finished.");
}
int getFileSize(String fileName) {
try {
URL url = new URL(getCodeBase(), fileName);
InputStream inStream = url.openStream();
BufferedInputStream fontStream = new BufferedInputStream(inStream);
int size = 0;
while (fontStream.read() != -1) {
size++;
}
fontStream.close();
return size;
} catch (IOException e) {
return 0;
}
}
void loadMany(int oneFont, int fontCnt, String fileName) {
System.out.println("fontcnt= " + fontCnt);
Font[] fonts = new Font[fontCnt];
int totalSize = 0;
boolean gotException = false;
for (int i=0; i<fontCnt; i++) {
try {
URL url = new URL(getCodeBase(), fileName);
InputStream inStream = url.openStream();
BufferedInputStream fontStream =
new BufferedInputStream(inStream);
fonts[i] = Font.createFont(Font.TRUETYPE_FONT, fontStream);
totalSize += oneFont;
fontStream.close();
} catch (Throwable t) {
gotException = true;
System.out.println("Applet " + id + " " + t);
}
}
if (!gotException) {
throw new RuntimeException("No expected exception");
}
}
}
/*
* Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
import java.io.*;
import java.awt.*;
public class DeleteFont {
public static void main(String args[]) throws Exception {
String font = "A.ttf";
String sep = System.getProperty("file.separator");
String testSrc = System.getenv("TESTSRC");
if (testSrc != null) {
font = testSrc + sep + font;
}
System.out.println("Using font file: " + font);
FileInputStream fis = new FileInputStream(font);
Font f = Font.createFont(Font.TRUETYPE_FONT, fis);
f.toString();
f.deriveFont(Font.BOLD);
f.canDisplay('X');
InputStream in = new InputStream() {
public int read() {
throw new RuntimeException();
}
};
boolean gotException = false;
try {
Font.createFont(java.awt.Font.TRUETYPE_FONT, in);
} catch (IOException e) {
gotException = true;
}
if (!gotException) {
throw new RuntimeException("No expected IOException");
}
badRead(-2);
badRead(8193);
}
static void badRead(final int retval) {
int num = 2;
byte[] buff = new byte[16*8192]; // Multiple of 8192 is important.
for (int ct=0; ct<num; ++ct) {
try {
Font.createFont(
Font.TRUETYPE_FONT,
new ByteArrayInputStream(buff) {
@Override
public int read(byte[] buff, int off, int len) {
int read = super.read(buff, off, len);
return read<0 ? retval : read;
}
}
);
} catch (Throwable exc) {
//exc.printStackTrace();
}
}
}
}
# Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
# @test
# @bug 6189812 6380357 6632886
# @summary Verify that temporary font files are deleted on exit.
if [ -z "${TESTSRC}" ]; then
echo "TESTSRC undefined: defaulting to ."
TESTSRC=.
fi
if [ -z "${TESTCLASSES}" ]; then
echo "TESTCLASSES undefined: defaulting to ."
TESTCLASSES=.
fi
if [ -z "${TESTJAVA}" ]; then
echo "TESTJAVA undefined: can't continue."
exit 1
fi
echo "TESTJAVA=${TESTJAVA}"
echo "TESTSRC=${TESTSRC}"
echo "TESTCLASSES=${TESTCLASSES}"
cd ${TESTSRC}
${TESTJAVA}/bin/javac -d ${TESTCLASSES} DeleteFont.java
cd ${TESTCLASSES}
numfiles0=`ls ${TESTCLASSES} | wc -l`
${TESTJAVA}/bin/java -Djava.io.tmpdir=${TESTCLASSES} DeleteFont
if [ $? -ne 0 ]
then
echo "Test fails: exception thrown!"
exit 1
fi
numfiles1=`ls ${TESTCLASSES} | wc -l`
if [ $numfiles0 -ne $numfiles1 ]
then
echo "Test fails: tmp file exists!"
ls ${TESTCLASSES}
exit 1
fi
exit 0
<!--
Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation.
This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
CA 95054 USA or visit www.sun.com if you need additional information or
have any questions.
@test
@bug 6522586
@run applet bigfont.html
@summary Enforce limits on font creation
-->
<html>
<head>
<title>Test Font Creation Limits</title>
</head>
<body>
<hr>
<APPLET CODE = BigFont.class WIDTH = 100 HEIGHT = 100 >
<param name="number" value="1">
<param name="font" value="A.ttf">
</APPLET>
<APPLET CODE = BigFont.class WIDTH = 100 HEIGHT = 100>
<param name="number" value="2">
<param name="font" value="A.ttf">
</APPLET>
<hr>
</body>
</html>
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/*
* @test
* @bug 6652929
* @summary verify handling of File.getPath()
*/
import java.awt.*;
import java.io.*;
public class FontFile {
public static void main(String[] args) throws Exception {
String sep = System.getProperty("file.separator");
String fname = ".." + sep + "A.ttf";
String dir = System.getProperty("test.src");
if (dir != null) {
fname = dir + sep + fname;
}
final String name = fname;
System.out.println("Will try to access " + name);
if (!(new File(name)).canRead()) {
System.out.println("File not available : can't run test");
return;
}
System.out.println("File is available. Verify no access under SM");
System.setSecurityManager(new SecurityManager());
// Check cannot read file.
try {
new FileInputStream(name);
throw new Error("Something wrong with test environment");
} catch (SecurityException exc) {
// Good.
}
try {
Font font = Font.createFont(Font.TRUETYPE_FONT,
new File("nosuchfile") {
private boolean read;
@Override public String getPath() {
if (read) {
return name;
} else {
read = true;
return "somefile";
}
}
@Override public boolean canRead() {
return true;
}
}
);
System.err.println(font.getFontName());
throw new RuntimeException("No expected exception");
} catch (IOException e) {
System.err.println("Test passed.");
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册