Tk Source Code

View Ticket
Login
Ticket UUID: 55b95f578abe0ed31da250c6a9e105b42277f9a7
Title: Associating variable with bignum value with scale crashes it
Type: Bug Version: 8.6.6
Submitter: jal_frezie Created on: 2017-10-03 08:13:02
Subsystem: 15. [scale] Assigned To: fvogel
Priority: 5 Medium Severity: Severe
Status: Closed Last Modified: 2017-10-22 19:34:43
Resolution: Fixed Closed By: fvogel
    Closed on: 2017-10-22 19:34:43
Description:
$ wish
% pack [scale .s]
% set fooie 5.79e99
5.79e99
% .s config -variable fooie
*** buffer overflow detected ***: wish terminated
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x70bcb)[0x7f29259aabcb]
/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x37)[0x7f2925a33227]
/lib/x86_64-linux-gnu/libc.so.6(+0xf7360)[0x7f2925a31360]
/lib/x86_64-linux-gnu/libc.so.6(+0xf6919)[0x7f2925a30919]
/lib/x86_64-linux-gnu/libc.so.6(_IO_default_xsputn+0xac)[0x7f29259aebdc]
/lib/x86_64-linux-gnu/libc.so.6(+0x4be81)[0x7f2925985e81]
/lib/x86_64-linux-gnu/libc.so.6(_IO_vfprintf+0xf32)[0x7f2925981c32]
/lib/x86_64-linux-gnu/libc.so.6(__vsprintf_chk+0x8c)[0x7f2925a309ac]
....
7f2927dad000-7f2927dae000 rw-p 00024000 08:08 3014738                    /lib/x86_64-linux-gnu/ld-2.24.so
7f2927dae000-7f2927daf000 rw-p 00000000 00:00 0 
7ffc9d5d4000-7ffc9d5f5000 rw-p 00000000 00:00 0                          [stack]
7ffc9d5fa000-7ffc9d5fc000 r--p 00000000 00:00 0                          [vvar]
7ffc9d5fc000-7ffc9d5fe000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted
$
Haven't bothered to include whole stack trace as it is so easy to reproduce
This was on Linux, same thing happens on Mac OS but not Windows
User Comments: fvogel added on 2017-10-22 19:34:43:
Merged to core-8-6-branch and trunk

fvogel added on 2017-10-08 07:46:58:

I have now checked that the following script:

package require Tk
pack [scale .s]
.s configure -from -6.8e99
.s configure -to 8.8e99
set fooie 5.79e99
.s config -variable fooie

does no longer crash, on Windows, Linux, and OS X.

Also the scale.test runs without failure (including the added scale-21.[1|2] tests) on all the above three platforms.


fvogel added on 2017-10-07 20:21:28:

New test added to check non-crashing with huge values for -from and -to.

See [e7902b5a] in branch bug-55b95f578a


fvogel added on 2017-10-07 20:16:02:

Actually there are more places in the scale widget where similar issues occur. For instance consider the following snipped:

package require Tk
pack [scale .s]
.s configure -from -6.8e99
.s configure -to 8.8e99
The above used to crash before [9c78924b] I have just committed. In that patch, I used snprintf (with truncation of the result to TCL_DOUBLE_SPACE, if needed) because the format parameter needs to be used, otherwise the -digits option would have become ignored.

As mentioned by dgp three years ago in ticket [3417012]:

Longer term, Tk ought get out of the practice of using sprintf(), and if Tcl doesn't offer a public routine to replace it, it should.
(Tcl_PrintDouble() isn't quite right, due to the impact of tcl_precision.)
For now, though, segfaults are bad, and any fix is better than keeping one around.


fvogel added on 2017-10-07 19:05:06:

I decided to go for patch #2 below, see [794a08b2] in branch bug-55b95f578a.

The old and new values are compared as strings formatted by Tcl_PrintDouble. The printed strings are guaranteed to fit in their TCL_DOUBLE_SPACE size (since they are printed by Tcl_PrintDouble). There is no need to use the scalePtr->format parameter, Tcl_PrintDouble does the job.


fvogel added on 2017-10-07 18:33:12:

I confirm that backing out [2df13bc796] revives [891141ffff]. To demonstrate this, run the demo script "test.tcl" provided with [891141ffff], click on the movable slider of the scale (to give focus to the scale widget), and then move the mouse in and out the scale --> myCount increases. This does not happen with the fix [2df13bc796].


fvogel added on 2017-10-07 16:58:35:

I have prompted dgp (author of the fix [2df13bc796] of [891141ffff]) for comments in the chat. His proposal was to backout [2df13bc796] since Tcl numbers obey EIAS since 8.5.0 and [891141ffff] could have been a bug rooted in string round-tripping leading to a different value, which should no longer be an issue.

But backing out [2df13bc796] revives [891141ffff], so this is not an option.


fvogel added on 2017-10-04 05:16:29:

I have added a non-regression testcase in branch bug-55b95f578a


fvogel added on 2017-10-03 19:28:16:

Buffer overrun is as follows:

tkScale.c:678
        char varString[TCL_DOUBLE_SPACE]
        sprintf(varString, scalePtr->format, varValue);
The format is "%.0f", and the value is 5.79e99 --> a lot of digits are sprintfed, much more than TCL_DOUBLE_SPACE (which value is 27).

So this is a mismatch between the size of the string produced by sprintf, which can be very long due to the format parameter, and the size of the allocated buffer (buffers using TCL_DOUBLE_SPACE are supposed to be filled in by Tcl_PrintDouble, not by sprintf).

I believe this issue got created by [2df13bc796] which fixed [891141ffff]

I think we can fix this by one of the following two patches:

Patch 1:

Index: generic/tkScale.c
==================================================================
--- generic/tkScale.c
+++ generic/tkScale.c
@@ -19,10 +19,14 @@

 #include "default.h"
 #include "tkInt.h"
 #include "tkScale.h"

+#if defined(_WIN32)
+#define snprintf _snprintf
+#endif
+
 /*
  * The following table defines the legal values for the -orient option. It is
  * used together with the "enum orient" declaration in tkScale.h.
  */

@@ -675,11 +679,11 @@
                valuePtr, &varValue) != TCL_OK)) {
            ScaleSetVariable(scalePtr);
        } else {
            char varString[TCL_DOUBLE_SPACE], scaleString[TCL_DOUBLE_SPACE];

-           sprintf(varString, scalePtr->format, varValue);
-           sprintf(scaleString, scalePtr->format, scalePtr->value);
+           snprintf(varString, TCL_DOUBLE_SPACE, scalePtr->format, varValue);
+           snprintf(scaleString, TCL_DOUBLE_SPACE, scalePtr->format, scalePtr->value);
            if (strcmp(varString, scaleString)) {
                ScaleSetVariable(scalePtr);
            }
        }

Patch 2:

Index: generic/tkScale.c
==================================================================
--- generic/tkScale.c
+++ generic/tkScale.c
@@ -675,12 +675,12 @@
                valuePtr, &varValue) != TCL_OK)) {
            ScaleSetVariable(scalePtr);
        } else {
            char varString[TCL_DOUBLE_SPACE], scaleString[TCL_DOUBLE_SPACE];

-           sprintf(varString, scalePtr->format, varValue);
-           sprintf(scaleString, scalePtr->format, scalePtr->value);
+            Tcl_PrintDouble(NULL, varValue, varString);
+            Tcl_PrintDouble(NULL, scalePtr->value, scaleString);
            if (strcmp(varString, scaleString)) {
                ScaleSetVariable(scalePtr);
            }
        }
        Tcl_TraceVar2(interp, Tcl_GetString(scalePtr->varNamePtr),

I believe patch 1 is better because even if it limits the number of digits in the string rep to compare, it still uses the format parameter, which is not the case for patch 2.

A third approach, that would iteratively resize the buffer each time snprintf returns -1 (insufficient space), seems to be an overkill to me.


fvogel added on 2017-10-03 10:04:46:
Thanks for the report, I see what's wrong here. There is a buffer overrun, and I can see it on Windows as well. TCL_DOUBLE_SPACE is used to dimension a buffer that is populated by sprintf with a format parameter "%.0f", and with the huge number given the string rep of this number does not fit in TCL_DOUBLE_SPACE characters.