[PD-dev] [ pure-data-Patches-3420484 ] Invalid memory access in s_utf8.c

SourceForge.net noreply at sourceforge.net
Sun Oct 9 22:04:47 CEST 2011


Patches item #3420484, was opened at 2011-10-07 20:10
Message generated for change (Comment added) made by creamygoodness
You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=478072&aid=3420484&group_id=55736

Please note that this message will contain a full copy of the comment thread,
including the initial issue submission, for this request,
not just the latest update.
Category: puredata
Group: bugfix
Status: Open
Resolution: None
Priority: 5
Private: No
Submitted By: Marvin Humphrey (creamygoodness)
Assigned to: Nobody/Anonymous (nobody)
Summary: Invalid memory access in s_utf8.c

Initial Comment:
After hearing on IRC that segfaults could be produced while editing text in an
Object box under gdb, I tried running the Vanilla main git branch under
Valgrind:

    valgrind ./src/pd

Valgrind reported many "invalid read" memory errors, which on inspection, all 
arose from the same root cause: these strings are not NULL-terminated, but the 
UTF-8 handling code in s_utf8.c assumes NULL-termination and malfunctions in
its absence.

The attached patch suffices to eliminate the read errors. However, it does not 
address all the problems in s_utf8.c; other functions will require similar
treatment.

I have attempted make the new code as compatible with the old code as
possible, for instance preserving its dubious algorithm for handling missing
or excess continuation bytes.  Hopefully I got everything right, though it is
hard to be certain with bit-twiddling code like this in the absence of 
thorough unit tests.

----------------------------------------------------------------------

Comment By: Marvin Humphrey (creamygoodness)
Date: 2011-10-09 13:04

Message:
I have uploaded a new version of the patch for s_utf8.c, and also a new
version of the benchmarking app.  Highlights:

1. The benchmarking app now handles four different functions.
2. All changed functions have been sped up since the last patch,
especially u8_charnum().

u8_offset: 16% faster
u8_charnum: 22% faster
u8_inc: 10% faster

----------------------------------------------------------------------

Comment By: Hans-Christoph Steiner (eighthave)
Date: 2011-10-09 09:54

Message:
This sounds great, I'll include it in Pd-extended and we'll see how it does
there.  The only problem is that there are two patches included in this
tracker and they seem to have the same name.  Could you delete the older
one, so there is only one patch?

----------------------------------------------------------------------

Comment By: Marvin Humphrey (creamygoodness)
Date: 2011-10-08 17:08

Message:
In response to speed concerns raised in an IRC conversation ("well, its a
realtime engine that communicates thru the utf8 stuff, so its good to have
it
efficient"), I have prepared a benchmark program to test the performance
of
u8_offset().  It reads a file into RAM, then traverses it with u8_offset()
a
user-specified number of iterations.

Here are some results using the French wikipedia page on Pure Data as
source
material.

Without patch:

    marvin at smokey:~/projects/pure-data $ cc -Wall -Wextra -std=gnu99 -O3
bench.c src/s_utf8.c -o bench
    marvin at smokey:~/projects/pure-data $ time ./bench pd.html 10000
    pd.html (44683 bytes) 10000 iterations

    real	0m0.732s
    user	0m0.716s
    sys	0m0.003s

Running the test 10 times produced a range of 0.724s to 0.733s, so the
benchmark was pretty stable even on a Mac. :)

With patch:

    marvin at smokey:~/projects/pure-data $ cc -Wall -Wextra -std=gnu99 -O3
bench.c src/s_utf8.c -o bench
    marvin at smokey:~/projects/pure-data $ time ./bench pd.html 10000
    pd.html (44683 bytes) 10000 iterations

    real	0m0.618s
    user	0m0.612s
    sys	0m0.004s

So, in addition to eliminating the Valgrind warnings, the patched version
is
actually faster.  I speculate that this is because the current version of
u8_offset() needlessly traverses at least one extra byte when the header
byte of
the sequence is plain ASCII.

For what it's worth, if we remove the NUL-termination check from the loop
condition...

    -    while (charnum > 0 && str[offs]) {
    +    while (charnum > 0) {

We get even better:

    marvin at smokey:~/projects/pure-data $ time ./bench pd.html 10000
    pd.html (44683 bytes) 10000 iterations

    real	0m0.514s
    user	0m0.507s
    sys	0m0.004s

However, that could yield bogus results in the event that strlen(string)
differs from the buffer length held in a separate integer.  I would go
that
route in code which had been engineered to not need NUL-termination from
the
start, but wouldn't advocate for it here.

As a further test, I tried an implementation based on u8_seqlen() which
has
the advantage of being easier to understand, and is the algorithm
currently
used by the Perl core among others:

	while (charnum-- > 0 && str[offs]) {
		offs += u8_seqlen(str + offs);
	}

It performed substantially worse:

    marvin at smokey:~/projects/pure-data $ time ./bench pd.html 10000
    pd.html (44683 bytes) 10000 iterations

    real	0m1.906s
    user	0m1.891s
    sys	0m0.003s
    
In this case, I speculate that the lookup into the trailingBytesForUTF8
array
is costly, even though it doubtless gets into the hottest CPU cache and
stays
there.

The benchmarks were run on a 2007 Macbook Pro with a 2.4 GHz Core Duo
chipset running OS X 10.6 (Snow Leopard).

----------------------------------------------------------------------

You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=478072&aid=3420484&group_id=55736



More information about the Pd-dev mailing list