Subject: bug#20209: GUILE 2.0.11: crash in set_port_filename_x for bytevector ports

From: Mark H Weaver <[email-masked]>
Date: Sat, 28 Mar 2015 14:21:00 -0400

Hi Ludovic,

There's a problem with the C interfaces for bytevectors and binary
ports, introduced in 1ee2c72eafaae5f91f4c899bc4b4853af5c16f28.  Neither
of those modules are initialized until the associated Scheme modules are
loaded, but this is not documented.

There are undocumented public C API functions 'scm_init_r6rs_ports' and
'scm_init_bytevectors', but if they are called and the associated Scheme
modules (rnrs bytevectors) or (ice-9 binary-ports) are also loaded, then
those initialization functions will be called more than once.

In the case of 'scm_init_r6rs_ports', the duplicate initialization would
cause 'scm_make_port_type' to be called more than once, leading to
multiple copies of the same port types to be created.  The snarfed code
in r6rs-ports.x and bytevectors.x would also be run more than once.  I
don't know whether these duplicate initializations would cause problems,
but it seems like a bad idea.

Another issue is that 'scm_init_r6rs_ports' does not call
'scm_init_bytevectors', and yet that module calls C functions from
bytevectors.c.

For these reasons, I decided against recommending those undocumented
initialization functions.  Instead, I suggested that David initialize
binary ports by loading (ice-9 binary-ports):

  (void) scm_c_resolve_module ("ice-9 binary-ports");

For Guile 2.0.12, I suggest that we unconditionally do the equivalent of
'scm_init_bytevectors' and 'scm_init_r6rs_ports' during Guile
initialization, and make those functions into deprecated no-ops.

What do you think?

      Mark

Mark H Weaver <[email-masked]> writes:

> David Kastrup <[email-masked]> writes:
>
>> The following code
>>
>>
>> #include <libguile.h>
>>
>> int main()
>> {
>>   scm_init_guile ();
>>   SCM str = scm_c_make_bytevector (0);
>>   SCM port = scm_open_bytevector_input_port (str, SCM_UNDEFINED);
>>   scm_set_port_filename_x (port, scm_from_locale_string ("/usr/local/tmp/lilypond/ly/init.ly"));
>>   return 0;
>> }
>>
>>
>> crashes with the backtrace
>>
>> Program terminated with signal SIGSEGV, Segmentation fault.
>> #0  0xb7699059 in scm_iprlist () from /usr/lib/libguile-2.0.so.22
>> (gdb) bt
>> #0  0xb7699059 in scm_iprlist () from /usr/lib/libguile-2.0.so.22
>> #1  0xb7699e95 in ?? () from /usr/lib/libguile-2.0.so.22
>> #2  0xb7699153 in scm_iprlist () from /usr/lib/libguile-2.0.so.22
>> #3  0xb7699e95 in ?? () from /usr/lib/libguile-2.0.so.22
>> #4  0xb7699153 in scm_iprlist () from /usr/lib/libguile-2.0.so.22
>> #5  0xb7699e95 in ?? () from /usr/lib/libguile-2.0.so.22
>> #6  0xb7698ec9 in scm_prin1 () from /usr/lib/libguile-2.0.so.22
>> #7  0xb769a606 in scm_simple_format () from /usr/lib/libguile-2.0.so.22
>> #8  0xb76e5f0b in ?? () from /usr/lib/libguile-2.0.so.22
>> #9  0xb76e6539 in ?? () from /usr/lib/libguile-2.0.so.22
>> #10 0xb76e664c in scm_call_with_vm () from /usr/lib/libguile-2.0.so.22
>> #11 0xb764cc67 in scm_apply () from /usr/lib/libguile-2.0.so.22
>> #12 0xb764e90e in scm_apply_1 () from /usr/lib/libguile-2.0.so.22
>> #13 0xb76cdb7b in scm_throw () from /usr/lib/libguile-2.0.so.22
>> #14 0xb76ce04c in scm_ithrow () from /usr/lib/libguile-2.0.so.22
>> #15 0xb764b6a3 in scm_error_scm () from /usr/lib/libguile-2.0.so.22
>> #16 0xb764b778 in scm_error () from /usr/lib/libguile-2.0.so.22
>> #17 0xb764ba22 in scm_wrong_type_arg () from /usr/lib/libguile-2.0.so.22
>> #18 0xb76927ca in scm_set_port_filename_x () from /usr/lib/libguile-2.0.so.22
>> #19 0x08048723 in main () at nextbug.c:8
>>
>> after outputting
>>
>> guile: uncaught throw to wrong-type-arg: (set-port-filename! Wrong
>> type argument in position ~A: ~S (1 (Segmentation fault (core dumped)
>
> The problem turns out to be that binary ports are not initialized until
> the (ice-9 binary-ports) module is loaded.  As a result, the port
> returned by 'scm_open_bytevector_input_port' has a bad type tag.
>
> The workaround is to call scm_c_resolve_module ("ice-9 binary-ports")
> after initializing Guile before the first call to
> 'scm_open_bytevector_input_port'.  The following code works:
>
> #include <libguile.h>
>
> int main()
> {
>   scm_init_guile ();
>   (void) scm_c_resolve_module ("ice-9 binary-ports");
>   SCM str = scm_c_make_bytevector (0);
>   SCM port = scm_open_bytevector_input_port (str, SCM_UNDEFINED);
>   scm_set_port_filename_x (port, scm_from_locale_string ("/usr/local/tmp/lilypond/ly/init.ly"));
>   return 0;
> }
>
>       Mark