Menu

#977 Tcl wrong int64 parameter

None
closed-fixed
tcl (60)
5
2022-07-26
2009-01-04
Vera
No

I wrote a C function, expecting "long long" as parameter:
int c_my (long long big_offset);

With SWIG I have an interface to Tcl.

In Tcl I call:
c_my 3000000000

in C (c_my) I got:
big_offset = -1294967296.

There is an bug in SWIG_AsVal_long_SS_long.

The whole discussion is in comp.lang.tcl
Subj:"64 bit integers (long long)"

Here is my (working) hotfix. Replace all the code in SWIG_AsVal_long_SS_long with:

SWIGINTERN int
SWIG_AsVal_long_SS_long SWIG_TCL_DECL_ARGS_2(Tcl_Obj *obj, long long *val)
{
if (Tcl_GetWideIntFromObj(0, obj, val) != TCL_OK) {
return SWIG_TypeError;
}
return SWIG_OK;
}

Discussion

  • Kjell Wooding

    Kjell Wooding - 2010-12-01

    This is still a bug in 2.0.1.
    The problem is in tclprimtypes.swg:

    SWIGINTERN int
    SWIG_AsVal_dec(long long)(Tcl_Obj *obj, long long *val)
    {
    long v;
    if (Tcl_GetLongFromObj(0,obj, &v) == TCL_OK) {
    if (val) *val = v;
    return SWIG_OK;
    } else {
    ...

    When trying to convert a signed int64 (long long), this code attempts to convert first to a signed int32 (long). If the input is positive, but between 2^31 and 2^32, the conversion attempt will appear to succeed (at least, with the supplied test), but will cast the result to a negative quantity)

    The solution is to not do this silly optimization at all, or to require Tcl >=8.4, and change this call to Tcl_GetWideIntFromObj

    Patch attached. Please fix this, as spending a day chasing a bug that was identified back in 2009 is no kind of fun.

     
  • Kjell Wooding

    Kjell Wooding - 2010-12-01

    --- tclprimtypes.swg.orig 2010-12-01 12:11:23.000000000 -0700
    +++ tclprimtypes.swg 2010-12-01 12:30:40.000000000 -0700
    @@ -133,11 +133,11 @@
    SWIGINTERN int
    SWIG_AsVal_dec(long long)(Tcl_Obj *obj, long long *val)
    {
    - long v;
    - if (Tcl_GetLongFromObj(0,obj, &v) == TCL_OK) {
    - if (val) *val = v;
    - return SWIG_OK;
    - } else {
    + /*
    + * XXX Bug - remove incorrect optimization.
    + * Optimized fix needs GetWideIntFromObj (tcl >=8.4), so
    + * just remove it for now
    + */
    int len = 0;
    const char *nptr = Tcl_GetStringFromObj(obj, &len);
    if (nptr && len > 0) {
    @@ -157,7 +157,6 @@
    }
    }
    }
    - }
    return SWIG_TypeError;
    }
    }

     
  • Kjell Wooding

    Kjell Wooding - 2010-12-06

    Here's a regression test for this issue. Add the following as Examples/test-suite/ll_ofl.i:

    %module ll_ofl
    %inline {
    #include <string.h>
    #include <stdio.h>
    #include <err.h>

    void
    llofltest(long long a, char *as)
    {
    char s[22];
    snprintf(s, sizeof(s), "%lld", a);
    if (strcmp(s, as))
    errx(1, "long long convert fail: %s != %s\n", s, as);
    }
    }

    void llofltest(long long a, char *as);

    ---
    Add the test to Examples/test-suite/tcl/Makefile.in:

    --- Makefile.in.orig 2010-12-06 16:08:41.000000000 -0700
    +++ Makefile.in 2010-12-06 15:30:36.000000000 -0700
    @@ -15,6 +15,7 @@
    li_cwstring

    C_TEST_CASES += \
    + ll_ofl \
    li_cstring \
    li_cwstring

    And add a test to Examples/test-suite/tcl/ll_ofl_runme.tcl

    if [ catch { load ./ll_ofl[info sharedlibextension] ll_ofl} err_msg ] {
    puts stderr "Could not load shared object:\n$err_msg"
    }

    llofltest 300000000 300000000
    llofltest 3000000000 3000000000
    llofltest 30000000000 30000000000

     
  • Olly Betts

    Olly Betts - 2022-03-19

    It looks like this is probably still present (at least the code of SWIG_AsVal_dec(long long) is still as pre the fix here) but I think for linux at least it only affects 32 bit architectures, as otherwise sizeof(long) == sizeof(long long).

    Here's a regression test just adding a _runme.tcl for an existing testcase:

    diff --git a/Examples/test-suite/tcl/integers_runme.tcl b/Examples/test-suite/tcl/integers_runme.tcl
    new file mode 100644
    index 00000000..c04dd1e9
    --- /dev/null
    +++ b/Examples/test-suite/tcl/integers_runme.tcl
    @@ -0,0 +1,24 @@
    +if [ catch { load ./integers[info sharedlibextension] integers} err_msg ] {
    
    +   puts stderr "Could not load shared object:\n$err_msg"
    +}
    +
    +set val 3902408827
    +if {[signed_long_identity $val] != $val} {
    +    puts stderr "Runtime test 1 failed"
    +    exit 1
    +}
    +
    +if {[unsigned_long_identity $val] != $val} {
    +    puts stderr "Runtime test 2 failed"
    +    exit 1
    +}
    +
    +if {[signed_long_long_identity $val] != $val} {
    +    puts stderr "Runtime test 3 failed"
    +    exit 1
    +}
    +
    +if {[unsigned_long_long_identity $val] != $val} {
    +    puts stderr "Runtime test 4 failed"
    +    exit 1
    +}
    

    I'll find a 32 bit machine to test on.

    Tcl 8.4.0 was tagged 2002-09-10 so we could just require that now.

     

    Last edit: Olly Betts 2022-03-22
  • Olly Betts

    Olly Betts - 2022-03-19
    • assigned_to: Olly Betts
    • Group: -->
     
  • Olly Betts

    Olly Betts - 2022-03-22

    Reproduced on x86 - the first case above returns a negative value, as expected from the original report.

     
  • Olly Betts

    Olly Betts - 2022-07-26

    I've opened a PR with a fix: https://github.com/swig/swig/pull/2331

     
  • Olly Betts

    Olly Betts - 2022-07-26
    • status: open --> closed-fixed
     
  • Olly Betts

    Olly Betts - 2022-07-26

    Merged (https://github.com/swig/swig/commit/06d375cdab63842b223ea5b9091ad0336144e899) - fix should be in SWIG 4.1.0.

     

Log in to post a comment.

MongoDB Logo MongoDB