Skip to content

-Txdot: C prints its own string constants for HTML cells with STYLE="dashed" (out-of-bounds read in xdot_style)

Kind
Upstream C behaviour
Severity
low
Engine
all
Category
render
Cases
0
Seeds
·
Tracking
GitHub issuewon't fix
Labels
fidelityupstream-cwontfix

With -Txdot (and the other formats built on the xdot renderer: -Tdot output's _ldraw_ attributes, -Tjson), an HTML-like table cell with STYLE="dashed" or STYLE="dotted" makes C print a run of unrelated string constants from its own binary into the drawing: format strings, file names and assertion texts. The port prints the style the cell asked for (S 6 -dashed).

Reproducer

digraph {
  n [shape=box label=<<TABLE><TR><TD STYLE="dashed">x</TD></TR></TABLE>>]
}
GVBINDIR=<pango-free plugins> dot -Txdot repro.gv
graphviz-pure -Txdot repro.gv

C output vs port (C's line shortened)

-_ldraw_="S 5 -solid c 7 -#000000 p 4 17.5 4 17.5 32.8 36.5 32.8 36.5 4 S 1095 -dashed(dotted,invis,invisible,bold,setlinewidth,filled,unfilled,tapered,gvrender_set_style: unsupported style %s - ignoring\n,gvrender_usershape,gvrender.c,name,name[0],color %s,%s is not a known color. ...
+_ldraw_="S 5 -solid c 7 -#000000 p 4 17.5 4 17.5 32.8 36.5 32.8 36.5 4 S 6 -dashed c 7 -#000000 p 4 20.5 7 20.5 29.8 33.5 29.8 33.5 7 F 14 11 -Times-Roman c 7 -#000000 T 23.5 12.8 -1 7 1 -x ",

Root cause

doBorder (lib/common/htmltable.c:261-268) passes the style as sptr[0] = "dashed", a plain string literal. Style lists elsewhere come from parse_style, where each entry is name\0arg\0arg\0\0: a name followed by its arguments and an empty string. xdot_style (plugin/core/gvrender_core_dot.c:186-199) relies on that layout: after the name it skips one NUL and, while the next byte is not NUL, reads arguments. For the literal "dashed" the next bytes belong to whatever constants the compiler placed after it, so C reads past the end of the string (undefined behaviour) until it happens to hit two NULs in a row. What it prints depends on the binary's string-table layout.

The port builds the same style in crates/graphviz-pure-render/src/emit/html.rs:181 and prints just the name, which is what C means to do.

Why the port should not match: the C output is an out-of-bounds read; its content depends on the compiler and linker, not on the graph. gv-diff render does not include this case. It should be reported upstream: doBorder should pass a properly terminated style (e.g. "dashed\0\0") or xdot_style should not assume arguments follow.

Cases

Not a fuzz case: found while porting the renderer (docs/porting/render.md, Known differences). The fuzz generator does not produce HTML-like labels.

Files

tests/fuzz-open/034-html-cell-dashed-style-overread

C and the port

Comparing…

Reproduce

repro.gv
digraph {
n [shape=box label=<<TABLE><TR><TD STYLE="dashed">x</TD></TR></TABLE>>]
}
cmd.txt
GVBINDIR=$HOME/src/gv-ref/lutplugins dot -Txdot repro.gv
graphviz-pure -Txdot repro.gv
cases.txt
Not a fuzz case: found while porting the renderer (docs/porting/render.md, Known differences).

Files: c.xdot, cases.txt, cmd.txt, repro.gv, rust.xdot. Source: tests/fuzz-open/034-html-cell-dashed-style-overread.

All open issues