Menu

#118 Stack smashing detected on MSYS2/Clang64

2.0
closed
None
1
2025-06-22
2025-06-03
ofv
No

tkimg release 2.0.1

Running this under the Clang64 environment of MSYS2 (note that the clang64 environment is native Windows using UCRT, Universal C Runtime, which is the current C runtime of Microsoft).

# stack.tcl
puts stderr "starting..."
package require Img
image create photo milogo -file "cat.jpg"
puts "done"
exit

(replace "cat.jpg" with any jpg file)

$ wish stack.tcl
starting...
*** stack smashing detected ***: terminated

The tkimg package in the clang64 environment is built with the following flags, as displayed by tkimg's configure script:

  Installation directory:             /clang64
  Documentation directory:            ${prefix}/share/doc/tiff-4.7.0
  C compiler:                         clang -march=nocona -msahf -mtune=generic -O2 -pipe -Wp,-D_FORTIFY_SOURCE=2 -fstack-protector-strong -Wp,-D__USE_MINGW_ANSI_STDIO=1 -ggdb -Og -ffile-prefix-map=/d/dev/other/MINGW-packages/mingw-w64-tkimg/src=/clang64/src/debug/mingw-w64-tkimg -fsanitize=address -pipe -Wall -W
  C++ compiler:                       clang++ -march=nocona -msahf -mtune=generic -O2 -pipe -Wp,-D_FORTIFY_SOURCE=2 -fstack-protector-strong -Wp,-D__USE_MINGW_ANSI_STDIO=1 -ggdb -Og -ffile-prefix-map=/d/dev/other/MINGW-packages/mingw-w64-tkimg/src=/clang64/src/debug/mingw-w64-tkimg

Rebuilding with debug symbols and running under gdb shows this:

gdb: unknown target exception 0xc0000409 at 0x7fff2cc54327

Thread 1 received signal ?, Unknown signal.
0x00007fff2cc54327 in __fastfail (code=2) at C:/M/msys64/clang64/include/_mingw.h:632
warning: 632    C:/M/msys64/clang64/include/_mingw.h: No such file or directory
(gdb) bt
#0  0x00007fff2cc54327 in __fastfail (code=2) at C:/M/msys64/clang64/include/_mingw.h:632
#1  __stack_chk_fail () at C:/W/B/src/mingw-w64/mingw-w64-crt/ssp/stack_chk_fail.c:18
#2  0x00007fff2cc51ff1 in CommonMatch (png_ptr=0x0, handle=<optimized out>,
    widthPtr=0x7fff2cc5d148 <__stack_chk_guard>, heightPtr=0x7fff2cc5431e <__stack_chk_fail+46>,
    xdpiPtr=<optimized out>, ydpiPtr=<optimized out>) at ../../Img-2.0.1/png\png.c:562
#3  0x0000000000000000 in ?? ()

Checking out current tkimg svn source and building with:

$ configure --with-tcl=/clang64/lib --with-tk=/clang64/lib
$ make

then executing under gdb:

$ gdb --args wish stack.tcl
Thread 1 received signal SIGSEGV, Segmentation fault.
0x00007ffee7f26f3f in png_free () from C:\apps\msys64\clang64\lib\Img2.1.0\pngtcl1648.dll
(gdb) bt
#0  0x00007ffee7f26f3f in png_free () from C:\apps\msys64\clang64\lib\Img2.1.0\pngtcl1648.dll
#1  0x00007ffee7f24dd6 in png_destroy_gamma_table ()
   from C:\apps\msys64\clang64\lib\Img2.1.0\pngtcl1648.dll
#2  0x00007ffee7f298c7 in png_destroy_read_struct ()
   from C:\apps\msys64\clang64\lib\Img2.1.0\pngtcl1648.dll
#3  0x0000000000000000 in ?? ()

Please note the botched stack trace, which may indicate that the stack is corrupted.

Please let me know if you need more information. Thanks.

Discussion

  • Paul Obermeier

    Paul Obermeier - 2025-06-07

    I do not have MSYS2/Clang available, but running tests on Debian using similar compiler options (-fsanitize=address,undefined -Wp,-D_FORTIFY_SOURCE=2 -fstack-protector-strong -mshstk) on current trunk did not show any stack corruptions.
    Looking at the source of png.c, your first stack trace, where png_ptr is 0x0 in CommonMatch should not be possible, because png_ptr is checked just before calling CommonMatch.

    I tested with Tcl/Tk 9.0.1. Which version of Tcl/Tk are you using?
    Are you loading any other extensions, which may cause the stack corruption?

     
  • ofv

    ofv - 2025-06-07

    I'm using tcl/tk 8.6.16. There are no other extensions loaded, just the code above executed with wish.

    I tried to replicate the problem in Linux, to no avail, even with Valgrind, but please note that Windows and Linux have different ABIs and different C runtimes. It is interesting that the crash does not happen with the MinGW64 environment (which uses the old MSCVCRT runtime).

    All packages in MSYS2 (a few thousands of them) are built with -Wp,-D_FORTIFY_SOURCE=2 -fstack-protector-strong and AFAIK this is the first time someone reports an stack smashing crash.

    As soon as I have time I'll investigate the problem by placing breakpoints on those functions and watching what happens before the crash.

    Thanks for your interest.

     
  • ofv

    ofv - 2025-06-07

    This looks like Tk is trying to load the jpg file as if it were a png, isn't it?

    #0  png_error (png_ptr=png_ptr@entry=0x30fb890,
        error_message=0x7ff980e71222 <.refptr.png_sRGB_table+1418> "Not a PNG file")
        at D:/dev/other/tkimg-code/compat/libpng/pngerror.c:82
    #1  0x00007ff980e5431e in png_read_sig (png_ptr=png_ptr@entry=0x30fb890, info_ptr=<optimized out>)
        at D:/dev/other/tkimg-code/compat/libpng/pngrutil.c:139
    #2  0x00007ff980e48f1a in png_read_info (png_ptr=0x30fb890,
        info_ptr=0x7ff980e71222 <.refptr.png_sRGB_table+1418>)
        at D:/dev/other/tkimg-code/compat/libpng/pngread.c:103
    #3  0x00007ff9a0111d29 in CommonMatch (png_ptr=0x30fb890, handle=<optimized out>,
        widthPtr=widthPtr@entry=0x14f670, heightPtr=heightPtr@entry=0x14f66c,
        xdpiPtr=xdpiPtr@entry=0x14f3c8, ydpiPtr=<optimized out>)
        at D:/dev/other/tkimg-code/png/png.c:534
    #4  0x00007ff9a01114f1 in FileMatchVersion3 (interp=<optimized out>, chan=<optimized out>,
        metadataIn=0x0, heightPtr=0x14f66c, metadataOut=0x0, fileName=<optimized out>,
        format=<optimized out>, widthPtr=<optimized out>) at D:/dev/other/tkimg-code/png/png.c:438
    #5  FileMatch (chan=<optimized out>, fileName=<optimized out>, format=<optimized out>,
        widthPtr=0x14f670, heightPtr=0x14f66c, interp=0x5b37c0)
        at D:/dev/other/tkimg-code/png/png.c:495
    #6  0x00007ff9a0403abd in Tk_PhotoSetSize_Panic () from C:\apps\msys64\clang64\bin\tk86.dll
    #7  0x00007ff9a0401f65 in Tk_PhotoSetSize_Panic () from C:\apps\msys64\clang64\bin\tk86.dll
    #8  0x00007ff9a03fd39a in TkGetBitmapData () from C:\apps\msys64\clang64\bin\tk86.dll
    #9  0x00007ff9a03f1084 in Tk_ImageObjCmd () from C:\apps\msys64\clang64\bin\tk86.dll
    #10 0x00007ff9810d7d23 in Tcl_EvalObjv () from C:\apps\msys64\clang64\bin\tcl86.dll
    #11 0x00007ff9810d90b8 in TclEvalEx () from C:\apps\msys64\clang64\bin\tcl86.dll
    #12 0x00007ff981187b29 in Tcl_FSEvalFileEx () from C:\apps\msys64\clang64\bin\tcl86.dll
    #13 0x00007ff9a040bea3 in Tk_MainExW () from C:\apps\msys64\clang64\bin\tk86.dll
    --Type <RET> for more, q to quit, c to continue without paging--c
    #14 0x00007ff63ca71426 in wWinMain ()
    #15 0x00007ff63ca71ea9 in wmain ()
    #16 0x00007ff63ca7133d in WinMainCRTStartup ()
    

    Then it reaches the LONGJMP and the stack is corrupted:

    373         LONGJMP(info->jmpbuf,1);
    (gdb) p info->jmpbuf
    $5 = {{Part = {1372672, 140710109060340}}, {Part = {1372672, 0}}, {Part = {0, 0}}, {Part = {
          130306317948473, 140709739401360}}, {Part = {57604864, 1373436}}, {Part = {2,
          140709589829986}}, {Part = {17015796416037994159, 0}}, {Part = {2, 140709590224408}}, {
        Part = {54971584, 17015796416037994111}}, {Part = {0, 57604864}}, {Part = {54971584,
          140709589384401}}, {Part = {0, 0}}, {Part = {0, 0}}, {Part = {0, 0}}, {Part = {
          17015796416037994031, 0}}, {Part = {0, 57604864}}}
    (gdb) bt
    #0  tk_png_error (png_ptr=<optimized out>,
        error_msg=0x7ff980e71222 <.refptr.png_sRGB_table+1418> "Not a PNG file")
        at D:/dev/other/tkimg-code/png/png.c:373
    #1  0x00007ff980e45cdf in png_error (png_ptr=0xec2440afd45bcaff, png_ptr@entry=0x30fb890,
        error_message=0x1 <error: Cannot access memory at address 0x1>)
        at D:/dev/other/tkimg-code/compat/libpng/pngerror.c:83
    #2  0x00007ff980e5431e in png_read_sig (png_ptr=png_ptr@entry=0x30fb890, info_ptr=<optimized out>)
        at D:/dev/other/tkimg-code/compat/libpng/pngrutil.c:139
    #3  0x00007ff980e48f1a in png_read_info (png_ptr=0x30fb890, info_ptr=0x1)
        at D:/dev/other/tkimg-code/compat/libpng/pngread.c:103
    #4  0x00007ff9a0111d29 in CommonMatch (png_ptr=0x30fb890, handle=<optimized out>,
        widthPtr=widthPtr@entry=0x14f670, heightPtr=heightPtr@entry=0x14f66c,
        xdpiPtr=xdpiPtr@entry=0x14f3c8, ydpiPtr=<optimized out>)
        at D:/dev/other/tkimg-code/png/png.c:534
    #5  0x00007ff9a01114f1 in FileMatchVersion3 (interp=<optimized out>, chan=<optimized out>,
        metadataIn=0x0, heightPtr=0x14f66c, metadataOut=0x0, fileName=<optimized out>,
        format=<optimized out>, widthPtr=<optimized out>) at D:/dev/other/tkimg-code/png/png.c:438
    #6  FileMatch (chan=<optimized out>, fileName=<optimized out>, format=<optimized out>,
        widthPtr=0x14f670, heightPtr=0x14f66c, interp=0x5b37c0)
        at D:/dev/other/tkimg-code/png/png.c:495
    #7  0x00007ff9a0403abd in Tk_PhotoSetSize_Panic () from C:\apps\msys64\clang64\bin\tk86.dll
    #8  0x00007ff9a0401f65 in Tk_PhotoSetSize_Panic () from C:\apps\msys64\clang64\bin\tk86.dll
    #9  0x00007ff9a03fd39a in TkGetBitmapData () from C:\apps\msys64\clang64\bin\tk86.dll
    #10 0x00007ff9a03f1084 in Tk_ImageObjCmd () from C:\apps\msys64\clang64\bin\tk86.dll
    #11 0x00007ff9810d7d23 in Tcl_EvalObjv () from C:\apps\msys64\clang64\bin\tcl86.dll
    #12 0x00007ff9810d90b8 in TclEvalEx () from C:\apps\msys64\clang64\bin\tcl86.dll
    --Type <RET> for more, q to quit, c to continue without paging--q
    Quit
    (gdb) n
    CommonMatch (png_ptr=0x7ff9a0111d29 <CommonMatch+265>, handle=<optimized out>, widthPtr=0x14f280,
        heightPtr=0x7ff9a0118098 <pngtclStubsPtr>, xdpiPtr=0x30fb890, ydpiPtr=<optimized out>)
        at D:/dev/other/tkimg-code/png/png.c:529
    529         if (SETJMP((((cleanup_info *) png_get_error_ptr(png_ptr))->jmpbuf))) {
    (gdb) bt
    #0  CommonMatch (png_ptr=0x7ff9a0111d29 <CommonMatch+265>, handle=<optimized out>,
        widthPtr=0x14f280, heightPtr=0x7ff9a0118098 <pngtclStubsPtr>, xdpiPtr=0x30fb890,
        ydpiPtr=<optimized out>) at D:/dev/other/tkimg-code/png/png.c:529
    #1  0x0000000000000000 in ?? ()
    (gdb)
    

    I'm not much of a C programmer (C++, yes) so my knowledge of setjmp/longjmp is almost none, never used it. Does the above ring any bells?

    Looking at the build output, I see lots of warnings like this:

    D:/dev/other/tkimg-code/tiff/tiffJpeg.c:271:15: warning: incompatible pointer types passing
          'jmp_buf' (aka 'struct _SETJMP_FLOAT128[16]') to parameter of type 'void **'
          [-Wincompatible-pointer-types]
      271 |       LONGJMP(sp->exit_jmpbuf, 1); /* return to libtiff caller */
          |               ^~~~~~~~~~~~~~~
    

    BTW, the code I'm running under gdb is a svn checkout of tkimg configured with "-O0 -g", no extra options. A sample compiler invocation:

    gcc -DPACKAGE_NAME=\"tkimgtiff\" -DPACKAGE_TARNAME=\"tkimgtiff\" -DPACKAGE_VERSION=\"2.1.0\" -DPACKAGE_STRING=\"tkimgtiff\ 2.1.0\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE_URL=\"\" -DBUILD_tkimgtiff=/\*\*/ -DHAVE_STDIO_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_STRINGS_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_UNISTD_H=1 -DSTDC_HEADERS=1 -DTcl_Size=int -DTCL_THREADS=1 -DUSE_TCL_STUBS=1 -DUSE_TCLOO_STUBS=1 -DUSE_TK_STUBS=1 -DMODULE_SCOPE=extern\ __attribute__\(\(__visibility__\(\"hidden\"\)\)\) -DHAVE_HIDDEN=1 -DHAVE_NO_SEH=1 -DHAVE_CAST_TO_UNION=1 -DHAVE_STDBOOL_H=1 -DTCL_WIDE_INT_TYPE=long\ long -DHAVE_STRUCT_STAT64=1 -DHAVE_LSEEK64=1 -DTCL_CFG_OPTIMIZED=1 -DUSE_TCL_STUBS=1 -DUSE_TK_STUBS=1 -DTCL_MAJOR_VERSION=8 -DTKIMG_VERSION=\"2.1.0\" -DTIFFTCL_VERSION=\"4.7.0\" -DJPEGTCL_VERSION=\"9.6.0\" -DZLIBTCL_VERSION=\"1.3.1\" -DZLIB_INTERNAL -I"D:/dev/other/tkimg-code/tiff" -I"D:/dev/other/tkimg-code/base" -I"D:/dev/other/tkimg-code/libtiff" -I"D:/dev/other/tkimg-code/build/libtiff/libtiff" -I"D:/dev/other/tkimg-code/libjpeg" -I"D:/dev/other/tkimg-code/build/libjpeg/libjpeg" -I"D:/dev/other/tkimg-code/zlib" -I"D:/dev/other/tkimg-code/build/zlib" -I. -I"C:/apps/msys64/clang64/include" -I"C:/apps/msys64/clang64/include"   -DPACKAGE_TCLNAME=\"img::tiff\"  -O0 -g -pipe -O2 -fomit-frame-pointer -DNDEBUG -Wall   -c `cygpath -m ../../tiff/tiffPixar.c` -o tiffPixar.o
    

    (Please note that in the MSYS2 Clang64 environment gcc is an alias for clang)

     
  • ofv

    ofv - 2025-06-07

    Changing base/tkimg.h to use plain setjmp/longjmp instead of the __builtin variants removes the crash, as well as the warnings. I'll ask some people about what's going on and come back with a patch. Thank you.

     

    Last edit: ofv 2025-06-07
  • ofv

    ofv - 2025-06-08

    We fixed the problem by applying the patch below. It also modifies tif_ojpeg.c for consistency.

    Do you remember why mingw32 was changed to use __builtin_setjmp/__builtin_longjmp?

    Index: base/tkimg.h
    ===================================================================
    --- a/base/tkimg.h  (revision 957)
    +++ b/base/tkimg.h  (working copy)
    @@ -24,7 +24,7 @@
     #    define tkimg_vsnprintf vsnprintf
     #endif /* _MSC_VER_ */
    
    -#if defined(__MINGW32__)
    +#if defined(__MINGW32__) && !defined(__aarch64__) && !defined(_UCRT)
     #    define SETJMP(jbuf)        __builtin_setjmp(jbuf)
     #    define LONGJMP(jbuf, code) __builtin_longjmp(jbuf, code)
     #else
    Index: compat/libtiff/libtiff/tif_ojpeg.c
    ===================================================================
    --- a/compat/libtiff/libtiff/tif_ojpeg.c    (revision 957)
    +++ b/compat/libtiff/libtiff/tif_ojpeg.c    (working copy)
    @@ -152,8 +152,13 @@
      */
    
     /* define LIBJPEG_ENCAP_EXTERNAL */
    -#define SETJMP(jbuf) setjmp(jbuf)
    -#define LONGJMP(jbuf, code) longjmp(jbuf, code)
    +#if defined(__MINGW32__) && !defined(__aarch64__) && !defined(_UCRT)
    +#    define SETJMP(jbuf)        __builtin_setjmp(jbuf)
    +#    define LONGJMP(jbuf, code) __builtin_longjmp(jbuf, code)
    +#else
    +#    define SETJMP(jbuf)        setjmp(jbuf)
    +#    define LONGJMP(jbuf, code) longjmp(jbuf, code)
    +#endif
     #define JMP_BUF jmp_buf
     #define OJPEG_BUFFER 2048
     /* define EGYPTIANWALK */
    
     
  • Paul Obermeier

    Paul Obermeier - 2025-06-08

    The commit message from 2023-04-23 says:

    Added macros SETJMP and LONGJMP as workaround for incorrect setjmp and longjmp in MINGW.

    For a discussion of the error see ex. [https://stackoverflow.com/questions/53709069/setjmp-longjmp-in-x86-64-w64-mingw32]

    I don't know, which gcc versions are affected by the error.
    If only "old" compilers are affected (ex. gcc 4.x), a more clean and simple solution would be:

    #if defined(__MINGW32__) && __GNUC__ < 5
    #define SETJMP(jbuf) __builtin_setjmp(jbuf)
    #define LONGJMP(jbuf, code) __builtin_longjmp(jbuf, code)
    #else
    #define SETJMP(jbuf) setjmp(jbuf)
    #define LONGJMP(jbuf, code) longjmp(jbuf, code)
    #endif
    

    Another advantage of this change would be, that we do not need to change the libtiff source code.

    I have several MinGW gcc versions ranging from 4.9.2 to 14.2 available and
    could check the next days, what versions are affected.

    How does Clang report it's version number?

     
  • ofv

    ofv - 2025-06-08

    Thanks for the reference.

    oscar@w10x64-vm-sky CLANG64 /d
    $ echo | clang -dM -E - | grep clang
    #define __clang__ 1
    #define __clang_literal_encoding__ "UTF-8"
    #define __clang_major__ 20
    #define __clang_minor__ 1
    #define __clang_patchlevel__ 5
    #define __clang_version__ "20.1.5 "
    #define __clang_wide_literal_encoding__ "UTF-16"
    
    oscar@w10x64-vm-sky CLANG64 /d
    $ echo | clang -dM -E - | grep -i gnu
    #define __GNUC_MINOR__ 2
    #define __GNUC_PATCHLEVEL__ 1
    #define __GNUC_STDC_INLINE__ 1
    #define __GNUC__ 4
    

    So GNUC < 5 alone will not cut it. Besides, we don't know for sure if recent gcc is fixed.

    To be fair, my patch assumes that gcc with the Universal C Runtime will work. I didn't verify that. Both the patched and unpatched tkImg 2.0.1, built with gcc/UCRT do not crash.

    Maybe the less risky thing is

    -#if defined(__MINGW32__)
    +#if defined(__MINGW32__) && !defined(__aarch64__) && !defined(__clang__)
    

    ?

     
  • Paul Obermeier

    Paul Obermeier - 2025-06-08

    I used the following script and attached (incorrect) TIFF file for testing.
    Note, that the TIFF file is marked as suspicious (TIFF.CVE-2017-3042) by the
    TACHYON virus scanner on VirusTotal.

    set tkVersion [package require Tk]
    puts "Tk : $tkVersion ([expr 8 * $tcl_platform(pointerSize)]-bit)"
    
    set imgVersion [package require Img]
    puts "Img: $imgVersion"
    
    set f id_003177,src_001927,op_havoc,rep_1.tif
    
    puts "Checking $f ..."
    set retVal [catch {set phImg [image create photo -file $f -format {TIFF -verbose 1} ]} err]
    if { $retVal == 0 } {
        puts "$f -> $phImg ([image width $phImg] x [image height $phImg])"
        label .l -image $phImg
        pack .l
    } else {
        puts "Error: $err"
    }
    bind . <Escape> exit
    

    Tested the following MinGW/gcc versions:

    7.2.0 8.1.0 11.2.0 12.2.0 13.2.0 14.2.0

    All of these crash when compiled with SETJMP/LONGJMP set to setjmp/longjmp.
    When using the __builtin* versions, correct error messages like shown below are issued:

    Tk : 8.6.16 (64-bit)
    Img: 2.1.0
    Checking id_003177,src_001927,op_havoc,rep_1.tif ...
    Reading image: id_003177,src_001927,op_havoc,rep_1.tif
            Size in pixel: 10 x 32
            Dots per inch: -1 x -1
            Page index   : 0
    Error: JPEGLib: Not a JPEG file: starts with 0x80 0x3f
    

    Gimp for example also issues the "Not a JPEG file" message.

    Could you try the script and see, if it works for you.

     
  • ofv

    ofv - 2025-06-08

    Tested with unpatched 2.0.1 on mingw64 (gcc/msvcrt), ucrt64 (gcc/ucrt), clang64 (clang/ucrt), plus patched 2.0.1 (clang/ucrt).

    Same output as yours on all cases.

     
  • Paul Obermeier

    Paul Obermeier - 2025-06-12

    Checked in tkimg.h with proposed change:
    #if defined(__MINGW32__) && !defined(__aarch64__) && !defined(__clang__)

    Works for all of my test systems.

     
  • ofv

    ofv - 2025-06-12

    For the record, the commit in Mingw-packages is cf52b888301d4e0f66cf5116b0d9f4955876cb54

    link to commit in github

    That commit included the proposed change to tif_ojpeg.c, which I reckon it is unnecessary.

    Thank you.

     
  • Paul Obermeier

    Paul Obermeier - 2025-06-22
    • status: open --> closed
    • assigned_to: Paul Obermeier
     
  • Paul Obermeier

    Paul Obermeier - 2025-06-22

    Fix included in new version 2.1.0.

     

Log in to post a comment.

MongoDB Logo MongoDB