Here's how to reproduce my problem (I'm using SVN 1.7.10, but probably any
version is affected):
Start with a simple command line, e.g.
$ svn ls http://svn.apache.org/repos/asf/subversion
but that requires some setting in ~/.sunversion/servers to work. E.g. if
I'm behind a proxy to the internet,
I have to set some http-proxy-* options, otherwise I'll get this error:
svn: E175002: Unable to connect to a repository at URL '
http://svn.apache.org/repos/asf/subversion'
svn: E175002: OPTIONS of 'http://svn.apache.org/repos/asf/subversion':
Could not resolve hostname `svn.apache.org': Host not found (
http://svn.apache.org)
So once we got the command line going, lets rephrase the command with the
Perl bindings:
use SVN::Core;
use SVN::Client;
my $client = SVN::Client->new();
my $ls = $client->ls("http://svn.apache.org/repos/asf/subversion", undef,
0);
I run this script and get
RA layer request failed: Unable to connect to a repository at URL '
http://svn.apache.org/repos/asf/subversion': OPTIONS of '
http://svn.apache.org/repos/asf/subversion': Could not resolve hostname `
svn.apache.org': Host not found (http://svn.apache.org) at script.pl line 4.
Apparently, the SVN::Client doesn't honor the stuff in
~/.subversion/servers, though according
to the docs it should. Looking at SVN/Client.pm we see that if I don't
specify any parameter
to SVN::Client->new() then it will do
$client->config(SVN::Core::config_get_config(undef))
by default. This is the Perl equivalent of calling
svn_config_get_config(apr_hash_t **cfg_hash, NULL, pool)
and then assigning *cfg_hash to svn_client_ctx_t->config.
Note that in the Perl version the apr_hash_t returned by
svn_config_get_config
is first converted to a Perl hash and immediatley converted back to
an apr_hash_t by the setter function for svn_client_ctx_t->config.
Using gdb I confirmed that on return from svn_config_get_config
(on the C level) *cfg_hash points to an apr_hash_t with just two keys,
"config" and "servers" (and correct values). The corresponding
Perl hash also has keys "config" and "servers".
But the apr_hash_t that is actually passed down to something
like svn_ra_neon__open() (when svn_client_ls() is called in the
last line of the script) is corrupted: it contains two keys,
one is "config", but the other is purported to be 7 characters long,
but the contents is not "servers" (in my case, it's an empty string).
The value for the corrupted key is the same as for the original "servers",
though.
But neon will not be able to find the information for "servers",
hence the request will ultimately fail.
So it looks like the conversion from Perl hash to apr_hash_t is to
blame. It does NOT actually copy the key strings (in Perl
controlled memory) to some APR pool, but instead stores
their pointers into the apr_hash_t. If Perl later garbage collects
the key strings, the apr_hash_t will reference overwritten memory.
Since key strings exist only once in memory (i.e. all keys "foo"
in any hash in the program reference the same memory location),
the can only be garbage collected if they are dropped from all
existing hashes. Conversely, if I add the line
my %dummy = ( "servers" => undef );
before SVN::Client->new(), this will "pin" the hash key "servers"
for the rest of the execution. And indeed, this line will
make the script work!
Here's the proposed fix (for SVN 1.7.x)
--- subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c
+++ subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c
@@ -116,7 +116,7 @@
while (cnt--) {
SV* item = hv_iternextsv(h, &key, &retlen);
void *val = cv(item, ctx, pool);
- apr_hash_set(hash, key, APR_HASH_KEY_STRING, val);
+ apr_hash_set(hash, apr_pstrmemdup(pool, key, retlen), retlen, val);
}
return hash;
Received on 2013-07-25 12:40:16 CEST