File: tk/unix/tkUnixRFont.c
Function GetFont() calls
XftFontOpenPattern(...), which under some circumstances
*can* return NULL (I'm not sure if this can happen also
under sane conditions, or only on misconfigured systems)
GetFont passes on that pointer unchecked and unused.
Function InitFont() (same file) calls GetFont(), doesn't
check for NULL and dereferences the pointer.
This is where the crash happens.
Btw., function Tk_MeasureChars() also calls GetFont()
and passes its result directly to XftTextExtents32().
Whether the latter deals correctly with NULL-pointers
should be checked.
In Tk_DrawChars() the return-value of GetFont() is
correctly checked for NULL.
Logged In: YES
user_id=68433
From earlier reports
(slrncsfqms.g0f.avl@gamma.logic.tuwien.ac.at):
(gdb) bt
#0 0xb7f663ba in InitFont () from /usr/local/lib/libtk8.5.so
#1 0xb7f667ec in TkpGetFontFromAttributes () from
/usr/local/lib/libtk8.5.so
#2 0xb7f34717 in Tk_AllocFontFromObj () from
/usr/local/lib/libtk8.5.so
#3 0xb7f2cde3 in DoObjConfig () from /usr/local/lib/libtk8.5.so
#4 0xb7f2d091 in Tk_SetOptions () from
/usr/local/lib/libtk8.5.so
#5 0xb7f67447 in ConfigureButton () from
/usr/local/lib/libtk8.5.so
and <slrncsiet1.g0f.avl@gamma.logic.tuwien.ac.at>:
#0 0xb7f4d3fa in InitFont (tkwin=0x8159438, pattern=0x821f0f8,
fontPtr=0x8159738) at .../tk/unix/tkUnixRFont.c:187
187 if (XftPatternGetString(ftFont->pattern,
XFT_FAMILY, 0,
Logged In: YES
user_id=68433
Trouble is, at this point in the call sequence there's no
good way to signal an error. TkpGetFontFromAttributes()
can't return NULL, since the call sites in generic/tkFont.c
expect that it always succeeds.
And on the other end, it looks like FcFontRenderPrepare() is
always expected to succeed as well. Need to dig deeper into
the Xft documentation (such as it is...) to see if this is
really the case.
Logged In: NO
Maybe a dumb suggestion, but would it be possible to
attempt to fall back to some default font and return that,
And if even that fails, well it can't get worse than SEGV.
Lateron this could be extended to successively drop some
of user's wishes until a font can be found. Perhaps, even
falling back to core X fonts may be an option.
Logged In: YES
user_id=68433
More info:
FcFontRenderPrepare can return NULL, although AFAICT it only
does so in exceptional circumstances (out-of-memory,
internal misconfiguration).
XftFontOpenPattern() can return NULL if there is no
matching font; however, in this case the pattern in question
came from FcFontSort(), which AFAICT returns a list of
_available_ fonts. So it ought to match.
This looks like a "Can't happen" circumstance; leaving alone
for now since according to the original bug report on c.l.t.
this was happening on a system known to be misconfigured.
However, there are some other problems related to the Tk end
of things (see #800149) that I'm also investigating, after
fixing those things should be more robust.
Logged In: NO
I'm avl42 (logging in didn't seem to work)
There is some new/extra information:
My system is still Debian (woody then, now sarge), and the
misconfigured parts should be cleaned up by now.
My "wish8.5" has been configured with --enable-xft,
and on my system I "reconfigure" (using debian tools) the
package "fontconfig":
If I configure it to to use bitmap-fonts, and run a script that
uses {Helvetica 18} then wish crashes. If I setup fontconfig
not to use bitmapped fonts, then wish works, but the fonts
look lousy on my TFT.
I'd like to see it re-prioritized to at least 7.
As a solution, it would still be better to fall back to some default
font (e.g. fixed), than crash with a segfault. (see original post)
Logged In: YES
user_id=68433
Thanks for the followup. I'm also running Debian Sarge (or
almost-sarge), but still can't reproduce the problem.
I enabled bitmap fonts with dpkg-reconfigure; here's (what
seem to be) the relevant parts of
/var/cache/debconf/config.dat, does this match your system?
| Name: fontconfig/enable_bitmaps
| Value: true
| Name: fontconfig/hinting_type
| Value: Autohinter
| Name: fontconfig/rendering_type
| Value: Autohinter
| Name: fontconfig/subpixel_rendering
| Value: Automatic
I just tried a simple test script, 'pack [label .l -text Foo
-font {Helvetica 18}]'. This us definitely using a
bitmapped font now, but fails to crash ...
Does that script crash for you? If not, can you post one
that does?
Logged In: YES
user_id=830354
I don't seem to find a way to set the rendering_type using debconf
(although debconf level is set to lowest), which is
"Bytecode interpreter (CRT screens)" in my case, all others are
now like you described. and the script you suggested crashes
with a segfault. (it says, "Speicherzugriffsfehler", which is german
for "Segmentation fault"), and the exact location has already been
mentioned in this thread.
Hmm, perhaps there is still something left from my previously
broken system.
Anyway, other applications don't crash when using the same fonts, and
there must be something that can be done better than dereference a
possibly-NULL pointer.
Logged In: YES
user_id=68433
Originator: NO
Reported on the Tcl'ers chat:
| I think I just ran into bug # 1090382 with the font I downloaded from
| http://img.dafont.com/download/?os=win&file=graffiti_treat, in case you are still
| looking for a way to reproduce the problem
Still cannot replicate here though.
Logged In: YES
user_id=68433
Originator: NO
Same problem reported again on the Tcl'ers chat (System: NetBSD, right after an upgrade from 4.0-beta2 to 4.0-RC2).
After a long debugging session, turned out that running `fc-cache -fv` as root made the problem go away.
Logged In: YES
user_id=68433
Originator: NO
More info: the failure path was: in GetFont, FcFontRenderPrepare() returned a non-NULL pattern, but passing that to XftFontOpenPattern() returned NULL.
Logged In: YES
user_id=68433
Originator: NO
See also Debian bug #465462.
Logged In: YES
user_id=410366
Originator: NO
Attached font.diff is proof-of-concept patch which 1) runs through the whole set of subfonts returned by FcFontSort() (caching failures to minimise XftFontOpenPattern() calls); 2) tries to load "sans" font as a fallback. I hope that the idea will be useful.
Logged In: YES
user_id=410366
Originator: NO
Hm. Cannot attach a file. Including it here:
--- unix/tkUnixRFont.c.orig 2008-02-14 21:38:54.000000000 +0300
+++ unix/tkUnixRFont.c 2008-02-15 22:37:06.000000000 +0300
@@ -20,6 +20,7 @@
XftFont *ftFont;
FcPattern *source;
FcCharSet *charset;
+ int failed;
} UnixFtFace;
typedef struct {
@@ -60,6 +61,31 @@
}
static XftFont *
+GetFontFallback(
+ UnixFtFont *fontPtr)
+{
+ double size;
+ int pxsize;
+ char *type;
+
+ if (XftPatternGetDouble(fontPtr->pattern, XFT_SIZE, 0,
+ &size) == XftResultMatch) {
+ type = XFT_SIZE;
+ } else if (XftPatternGetInteger(fontPtr->pattern, XFT_PIXEL_SIZE, 0,
+ &pxsize) == XftResultMatch) {
+ type = XFT_PIXEL_SIZE;
+ size = pxsize;
+ } else {
+ type = XFT_SIZE;
+ size = 12.0;
+ }
+ return XftFontOpen(fontPtr->display, fontPtr->screen,
+ FC_FAMILY, FcTypeString, "sans",
+ type, XftTypeDouble, size,
+ NULL);
+}
+
+static XftFont *
GetFont(
UnixFtFont *fontPtr,
FcChar32 ucs4)
@@ -69,23 +95,57 @@
if (ucs4) {
for (i = 0; i < fontPtr->nfaces; i++) {
FcCharSet *charset = fontPtr->faces[i].charset;
- if (charset && FcCharSetHasChar(charset, ucs4)) {
- break;
+ if (!(charset && FcCharSetHasChar(charset, ucs4))) {
+ continue;
+ }
+ if (fontPtr->faces[i].ftFont) {
+ return fontPtr->faces[i].ftFont;
+ } else {
+ if (!fontPtr->faces[i].failed) {
+ FcPattern *pat = FcFontRenderPrepare(0, fontPtr->pattern,
+ fontPtr->faces[i].source);
+
+ fontPtr->faces[i].ftFont = XftFontOpenPattern(fontPtr->display, pat);
+ }
+ if (fontPtr->faces[i].ftFont) {
+ return fontPtr->faces[i].ftFont;
+ } else {
+ fontPtr->faces[i].failed = 1;
+ }
}
}
- if (i == fontPtr->nfaces) {
- i = 0;
+ }
+
+ for (i = 0; i < fontPtr->nfaces; i++) {
+ if (fontPtr->faces[i].ftFont) {
+ return fontPtr->faces[i].ftFont;
+ } else {
+ if (!fontPtr->faces[i].failed) {
+ FcPattern *pat = FcFontRenderPrepare(0, fontPtr->pattern,
+ fontPtr->faces[i].source);
+
+ fontPtr->faces[i].ftFont = XftFontOpenPattern(fontPtr->display, pat);
+ }
+ if (fontPtr->faces[i].ftFont) {
+ return fontPtr->faces[i].ftFont;
+ } else {
+ fontPtr->faces[i].failed = 1;
+ }
}
- } else {
- i = 0;
}
- if (!fontPtr->faces[i].ftFont) {
- FcPattern *pat = FcFontRenderPrepare(0, fontPtr->pattern,
- fontPtr->faces[i].source);
- fontPtr->faces[i].ftFont = XftFontOpenPattern(fontPtr->display, pat);
+ if (fontPtr->faces[fontPtr->nfaces].ftFont) {
+ return fontPtr->faces[fontPtr->nfaces].ftFont;
+ } else {
+ fontPtr->faces[fontPtr->nfaces].ftFont = GetFontFallback(fontPtr);
+ if (fontPtr->faces[fontPtr->nfaces].ftFont) {
+ return fontPtr->faces[fontPtr->nfaces].ftFont;
+ } else {
+ Tcl_Panic("Cannot find a usable font. Check your fontconfig settings");
+ /* Never reached */
+ return NULL;
+ }
}
- return fontPtr->faces[i].ftFont;
}
/*
@@ -184,7 +244,7 @@
FcCharSet *charset;
FcResult result;
XftFont *ftFont;
- int i;
+ int i, nfont;
if (!fontPtr) {
fontPtr = (UnixFtFont *) ckalloc(sizeof(UnixFtFont));
@@ -199,21 +259,22 @@
set = FcFontSort(0, pattern, FcTrue, NULL, &result);
if (!set) {
- FcPatternDestroy(pattern);
- ckfree((char *)fontPtr);
- return NULL;
+ nfont = 0;
+ } else {
+ nfont = set->nfont;
}
fontPtr->fontset = set;
fontPtr->pattern = pattern;
- fontPtr->faces = (UnixFtFace *) ckalloc(set->nfont * sizeof(UnixFtFace));
- fontPtr->nfaces = set->nfont;
+ /* Extra slot for possible fallback */
+ fontPtr->faces = (UnixFtFace *) ckalloc((nfont + 1) * sizeof(UnixFtFace));
+ fontPtr->nfaces = nfont;
/*
* Fill in information about each returned font
*/
- for (i = 0; i < set->nfont; i++) {
+ for (i = 0; i < nfont; i++) {
fontPtr->faces[i].ftFont = 0;
fontPtr->faces[i].source = set->fonts[i];
if (FcPatternGetCharSet(set->fonts[i], FC_CHARSET, 0,
@@ -222,7 +283,13 @@
} else {
fontPtr->faces[i].charset = 0;
}
+ fontPtr->faces[i].failed = 0;
}
+ /* Fill in a fallback slot */
+ fontPtr->faces[nfont].ftFont = 0;
+ fontPtr->faces[nfont].source = 0;
+ fontPtr->faces[nfont].charset = 0;
+ fontPtr->faces[nfont].failed = 0;
fontPtr->display = Tk_Display(tkwin);
fontPtr->screen = Tk_ScreenNumber(tkwin);
@@ -253,7 +320,7 @@
Tk_ErrorHandler handler = Tk_CreateErrorHandler(display, -1, -1, -1, NULL,
(ClientData) NULL);
- for (i = 0; i < fontPtr->nfaces; i++) {
+ for (i = 0; i <= fontPtr->nfaces; i++) {
if (fontPtr->faces[i].ftFont) {
XftFontClose(fontPtr->display, fontPtr->faces[i].ftFont);
}
Logged In: YES
user_id=68433
Originator: NO
More info: reported on the Tcl'ers chat:
This problem affects Tk on NetBSD, where the base system includes one version of Xft and another is available through pkgsrc. Tk picks up the base version, because the configure script tries (old, obsolete) `xft-config` first, and only checks (new, recommended) `pkg-config` second. Other toolkits that use the xft/fontconfig libs installed with pkgsrc do not have a problem; however xterm and a few others (which use the libs from the base system) are also prone to dumping core (`xterm -fa bitstream` is reported to crash, for example).
Logged In: YES
user_id=80530
Originator: NO
here is the workaround patch
from teopetuk in the form of
an attached file generated
by `cvs diff`
The patch stops the segfault
problem for me.
File Added: 1090382.patch
Logged In: YES
user_id=80530
Originator: NO
The Tk HEAD crashes in
canvText.test on my
CentOS 5 system. I'd like
to release Tk 8.5.2 soon, but
this crash limits my testing
ability, and gives me pause.
Logged In: YES
user_id=80530
Originator: NO
Stack trace for the crash:
Program received signal SIGSEGV, Segmentation fault.
0x080f5914 in GetTkFontAttributes (ftFont=0x0, faPtr=0x8a220c4)
at /home/dgp/cvs/tk/unix/../unix/tkUnixRFont.c:107
107 (void)XftPatternGetString(ftFont->pattern, XFT_FAMILY, 0, familyPtr);
(gdb) bt
#0 0x080f5914 in GetTkFontAttributes (ftFont=0x0, faPtr=0x8a220c4)
at /home/dgp/cvs/tk/unix/../unix/tkUnixRFont.c:107
#1 0x080f5d54 in InitFont (tkwin=0x8824360, pattern=0x8a21388,
fontPtr=0x8a220a0) at /home/dgp/cvs/tk/unix/../unix/tkUnixRFont.c:241
#2 0x080f5f55 in TkpGetNativeFont (tkwin=0x8824360,
name=0x8a21da8 "-adobe-times-medium-r-normal--*-200-*-*-*-*-*-*")
at /home/dgp/cvs/tk/unix/../unix/tkUnixRFont.c:303
#3 0x081230c2 in Tk_AllocFontFromObj (interp=0x87c5628, tkwin=0x8824360,
objPtr=0x89fdcd8) at /home/dgp/cvs/tk/unix/../generic/tkFont.c:1175
#4 0x081227fc in Tk_FontObjCmd (clientData=0x8824360, interp=0x87c5628,
objc=4, objv=0x88d29a4) at /home/dgp/cvs/tk/unix/../generic/tkFont.c:761
#5 0x08153a8a in TclEvalObjvInternal (interp=0x87c5628, objc=4,
objv=0x88d29a4,
command=0x89b0a1e "font metrics $font -linespace]\nset ax [font measure $font 0]\n\n\nforeach test {\n {-anchor nw nw xyz {bad anchor position \"xyz\": must be n, ne, e, se, s, sw, w, nw, or center}}\n {-fill #ff0000 #ff0"...,
length=29, flags=0) at /home/dgp/cvs/tcl/unix/../generic/tclBasic.c:3650
Appears that ftFont is NULL when it must not be.
Logged In: YES
user_id=68433
Originator: NO
dgp reports: canvTest.text segfaults at line 23.
One other thing to try: env FC_DEBUG=129 ./wish8.5
entry .e
# fontconfig should spit out a bunch of diagnostic info
.e configure -font -adobe-times-medium-r-normal--*-200-*-*-*-*-*-*
# ... and some more here.
# ... then you should segfault.
The problem is almost certainly due to a misconfigured font cache. `fc-cache -fv` usually fixes it, but _don't do that yet_ -- catching this thing in the wild is difficult.
I'd like to try a simpler patch (in preparation) to see if that fixes the problem.
Logged In: YES
user_id=68433
Originator: NO
Attached patch tk-xft-crash-1.patch takes a more conservative approach. It adds a fallback case to GetFont(), but omits all the "try harder" logic. I believe this will avoid the crashes dgp is observing. It does not account for cases where FcFontSort() fails.
File Added: tk-xft-crash-1.patch