From afc66428ba2662d9ea1e32c59f3b36fb1c1c97af Mon Sep 17 00:00:00 2001 From: prr Date: Mon, 5 Jan 2009 11:28:43 -0800 Subject: [PATCH] 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 --- src/share/classes/java/awt/Font.java | 179 ++++++++++++------ .../classes/sun/font/CreatedFontTracker.java | 54 ++++++ src/share/classes/sun/font/FileFont.java | 17 +- src/share/classes/sun/font/FontManager.java | 11 +- test/java/awt/FontClass/CreateFont/A.ttf | Bin 0 -> 2348 bytes .../awt/FontClass/CreateFont/BigFont.java | 139 ++++++++++++++ .../awt/FontClass/CreateFont/DeleteFont.java | 83 ++++++++ .../awt/FontClass/CreateFont/DeleteFont.sh | 66 +++++++ .../awt/FontClass/CreateFont/bigfont.html | 48 +++++ .../CreateFont/fileaccess/FontFile.java | 83 ++++++++ 10 files changed, 617 insertions(+), 63 deletions(-) create mode 100644 src/share/classes/sun/font/CreatedFontTracker.java create mode 100644 test/java/awt/FontClass/CreateFont/A.ttf create mode 100644 test/java/awt/FontClass/CreateFont/BigFont.java create mode 100644 test/java/awt/FontClass/CreateFont/DeleteFont.java create mode 100644 test/java/awt/FontClass/CreateFont/DeleteFont.sh create mode 100644 test/java/awt/FontClass/CreateFont/bigfont.html create mode 100644 test/java/awt/FontClass/CreateFont/fileaccess/FontFile.java diff --git a/src/share/classes/java/awt/Font.java b/src/share/classes/java/awt/Font.java index 8dc5938d4..3ed40325b 100644 --- a/src/share/classes/java/awt/Font.java +++ b/src/share/classes/java/awt/Font.java @@ -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 Font using the specified font type * and input data. The new Font 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() { + 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() { + 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() { + 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); } /** diff --git a/src/share/classes/sun/font/CreatedFontTracker.java b/src/share/classes/sun/font/CreatedFontTracker.java new file mode 100644 index 000000000..741337d5b --- /dev/null +++ b/src/share/classes/sun/font/CreatedFontTracker.java @@ -0,0 +1,54 @@ +/* + * 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; + } +} diff --git a/src/share/classes/sun/font/FileFont.java b/src/share/classes/sun/font/FileFont.java index b6a2099d2..5aad11b2a 100644 --- a/src/share/classes/sun/font/FileFont.java +++ b/src/share/classes/sun/font/FileFont.java @@ -1,5 +1,5 @@ /* - * 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 diff --git a/src/share/classes/sun/font/FontManager.java b/src/share/classes/sun/font/FontManager.java index 09f181f12..29d14047f 100644 --- a/src/share/classes/sun/font/FontManager.java +++ b/src/share/classes/sun/font/FontManager.java @@ -1,5 +1,5 @@ /* - * 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 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) { diff --git a/test/java/awt/FontClass/CreateFont/A.ttf b/test/java/awt/FontClass/CreateFont/A.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f80f5c3d569c810493a9fbaf7141a63831733764 GIT binary patch literal 2348 zcmbVO&2JM&6n``O>5tg!+OZ8Ju*6B67Kmdzu@eZ5A>f26!6qam0n%iX7&}rN8^;NW z+5=T7qF*Yg66&Fss;9P53qs;ULRDG`Rh4>aFTHT!*gt^wfH3Zx-EDxNwrbyc=KX$e z-p>4HcXq}=0Kf@tz`=Uzcqo$kYWxcTS;gpybnn3Mz}LUrL%xDMHj~$i@CUsNAl^lO ze`dL?-W<7o8h~3zUU#OL&A%P}K81V@xhFfndS=h!XE^{~0igZ4+5F0tmS7P;`UKlA zS4NH^stYxw-~>G5-UMwH8a6;yvys2kN_zyd8_5G-GYpUZ^(_ z0R<*aOu+%mCgz|Y)=kV~4(pYf07p%0~3&8)Wj4dD43Xo zM)=gkJmy?5u>b)gn^**yoHeo0KL>=jVBbu?ferlzHuM|V&~IQvzkv%oE)mw{@ z)ALk(@U0LYSt!qy=QFb~qG!!v<~+N9D3e{9*Ge#iBnwLz)S&ct$%EQVsgTdi&S@_w zQ#bAz#2#i)LILu+z&YgqDi1R@3Hzt9Iz!=bsH+S9?@*l(f-qho=z^{d;hO|Ng23+O z!|FAz>2N^H`2)GBLwj(Y$5HEU*a}V(C2+E2T)=~vut<_95`xR_!`r0#&f(mljo%H9 zCSlhNfyeqfr5L}?^H>9g9?ZYVskoLTIFV3^EGH~f;yD7Mz;RUId5}b&P{OhGoLmmY z{m|1L>Q*Z3M?&2dB$aLmw~-^cJGe0ABRTC_VrZ<>6~6;=b-i;u>e}gQin;Vq^)!L7tF(X_-gOujyG`qats(Gw%X$A^v$BoDRGDU4xg z3}YBd4@A5ma{C*&qpxtCazD3!8&x{^-RXYbB5xOJ8u@)5-oKC308vcEJ)w$%moB$L zLX{B8#!9KQzG(1bsMVaF0@}0F)7bQy#6Ux^xuZQE5G8~R#e7}{#afRy8i~g`+5-&| zHr9F=^F<^F(eq+auX{W3d41k!EE);Mg3Up1tw##_g1)#Em(T^##c7J~sI}H{({0KA z-j_R^t}QB0M5S)0XMQ}L=?b^o=;+`MDsekCJE9)TT*aOmpXm`_JF_>RFSMVX1LPrFW4L%88ih_kJsHFRWl@0Y z8{jd}rdmW!FpKSzO?n$HLEYBO9kGt>fEZcBeHhOvv|h7CCwfIN$1roer%js>>j@8w zP0{~Oc|0L_JY(@7LeFNP!og}4xe5n#&*mk9h?v6+K_5cFTDAxR2!g;_Mdn4$CSjT= z@pjYK$P!4B%sUJ(I4v^Bc$Z>j-YU4P7O+^X;!8H>ZKB&|1*_F2dhE>GrP>-B*labD z*YrMz9qe|8>~}Kn)K49985L1_m%fLJJ~BMH2NAXb;MvY9!=8Img9mztAi-ve-!qy~ z<2(ℜyHe$X3A=a4KE;YvWT*tD?oL)h8oIhlkqO(__Z&$MRa%9hh`>|BkPR{a1h+ zzaGW4_0-c8?qKZQQFBDRhf}MasAB@-4w~9ff>bwAkBE3W81tZb0w=(VZDVi?+sL1^ CwQRot literal 0 HcmV?d00001 diff --git a/test/java/awt/FontClass/CreateFont/BigFont.java b/test/java/awt/FontClass/CreateFont/BigFont.java new file mode 100644 index 000000000..c14802485 --- /dev/null +++ b/test/java/awt/FontClass/CreateFont/BigFont.java @@ -0,0 +1,139 @@ +/* + * 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 + + + + Test Font Creation Limits + + +
+ + + + + + + + +
+ + + diff --git a/test/java/awt/FontClass/CreateFont/fileaccess/FontFile.java b/test/java/awt/FontClass/CreateFont/fileaccess/FontFile.java new file mode 100644 index 000000000..444197489 --- /dev/null +++ b/test/java/awt/FontClass/CreateFont/fileaccess/FontFile.java @@ -0,0 +1,83 @@ +/* + * 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."); + } + } +} -- GitLab