- priority: 5 --> 2
- status: open --> closed-fixed
OriginalBugID: 2500 Bug
Version: 8.1.1
SubmitDate: '1999-08-04'
LastModified: '1999-08-04'
Severity: SER
Status: Released
Submitter: techsupp
ChangedBy: hobbs
OS: Other
OSVersion: FreeBSD sue.loverso.southborough.ma.us 3.2-19990604-STABLE FreeB
Machine: NA
FixedDate: '1999-08-04'
ClosedDate: '2000-10-25'
Name:
Amy Adams
Extensions:
None.
CustomShell:
None.
Comments:
N/A
ReproducibleScript:
We have some commands we have created that take any data and
an integer as arguments. We do not know the contents of the data
and simply wish to pass it through to a lower library. Thus,
we use Tcl_GetByteArrayFromObj as of 8.1 (since Tcl_GetStringFromObj
now inserts Unicode/UTF-8 stuff, thus changing our data). We
also take an integer arg that we retrieve through Tcl_GetIntFromObj.
The summary of the problem is that if the user uses data that happens
to be equal to the integer arg, Tcl destroys the memory allocated
and returned to us from Tcl_GetByteArrayFromObj when we do the
Tcl_GetIntFromObj call because Tcl is using the same obj for both
arguments. We then reference invalid memory and access random garbage.
Compile the following program into its own package and execute
it as:
<prompt> tclsh8.1
% load <path>/libdb_tcl.so
% berkdb 1 1
----
#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <tcl.h>
#endif
/*
* Prototypes for procedures defined later in this file:
*/
static int berkdb_Cmd __P((ClientData, Tcl_Interp *, int,
Tcl_Obj * CONST*));
int
Db_tcl_Init(interp)
Tcl_Interp *interp; /* Interpreter in which the
package is
* to be made available. */
{
int code;
code = Tcl_PkgProvide(interp, "Db_tcl", "1.0");
if (code != TCL_OK)
return (code);
Tcl_CreateObjCommand(interp, "berkdb", (Tcl_ObjCmdProc
*)berkdb_Cmd,
(ClientData)0, NULL);
return (TCL_OK);
}
/*
* For test purposes berkdb_Cmd takes 2 args, data and an id.
*/
int
berkdb_Cmd(notused, interp, objc, objv)
ClientData notused; /* Not used. */
Tcl_Interp *interp; /* Interpreter */
int objc; /* How many arguments? */
Tcl_Obj *CONST objv[]; /* The argument objects */
{
char *data;
int datalen;
int id;
if (objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "command cmdargs");
return (TCL_ERROR);
}
datalen = 0;
data = NULL;
id = 0;
data = Tcl_GetByteArrayFromObj(objv[1], &datalen);
printf("data is at 0x%x, contents 0x%x\n",data, (int) *data);
if (Tcl_GetIntFromObj(interp, objv[2], &id) != TCL_OK)
return(TCL_ERROR);
printf("data is at 0x%x, contents 0x%x\n",data, (int) *data);
return(TCL_OK);
}
ObservedBehavior:
Here is a script that illustrates the problem:
Script started on Wed Aug 4 14:51:36 1999
build_unix 101 >> gdb tclsh8.1
GNU gdb 4.18
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you
are
welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for
details.
This GDB was configured as "i386-unknown-freebsd"...
(gdb) run
Starting program: /usr/local/bin/tclsh8.1
% load /usr/local/BerkeleyDB/lib/libdb_tcl.so
% ^C
Program received signal SIGINT, Interrupt.
0x28153024 in read () from /usr/lib/libc.so.3
(gdb) break berkdb_Cmd
Breakpoint 1 at 0x281a6f54: file ../dist/../tcl/tcl_db_pkg.c, line 65.
(gdb) list berkdb_Cmd
55 berkdb_Cmd(notused, interp, objc, objv)
56 ClientData notused; /* Not used. */
57 Tcl_Interp *interp; /* Interpreter */
58 int objc; /* How many arguments?
*/
59 Tcl_Obj *CONST objv[]; /* The argument objects
*/
60 {
61 char *data;
62 int datalen;
63 int id;
64
(gdb)
65 if (objc != 3) {
66 Tcl_WrongNumArgs(interp, 1, objv, "command
cmdargs");
67 return (TCL_ERROR);
68 }
69 datalen = 0;
70 data = NULL;
71 id = 0;
72 data = Tcl_GetByteArrayFromObj(objv[1], &datalen);
73 printf("data is at 0x%x, contents 0x%x\n",data, (int)
*data);
74 if (Tcl_GetIntFromObj(interp, objv[2], &id) != TCL_OK)
(gdb)
75 return(TCL_ERROR);
76 printf("data is at 0x%x, contents 0x%x\n",data, (int)
*data);
77 return(TCL_OK);
78 }
(gdb) c
Continuing.
berkdb 1 1
Breakpoint 1, berkdb_Cmd (notused=0x0, interp=0x804d420, objc=3,
objv=0x8053020) at ../dist/../tcl/tcl_db_pkg.c:65
65 if (objc != 3) {
(gdb) print objc
$1 = 3
(gdb) print objv[0]
$2 = (Tcl_Obj *) 0x8065020
(gdb) print objv[1]
$3 = (Tcl_Obj *) 0x805eea0
(gdb) print objv[2]
$4 = (Tcl_Obj *) 0x805eea0
(gdb) print *objv[1]
$5 = {refCount = 9, bytes = 0x805de60 "1", length = 1, typePtr =
0x281006e4,
internalRep = {longValue = 1, doubleValue = 1.2217634361863799e+161,
otherValuePtr = 0x1, twoPtrValue = {ptr1 = 0x1, ptr2 =
0x61616161}}}
(gdb) n
69 datalen = 0;
(gdb) n
71 id = 0;
(gdb) n
72 data = Tcl_GetByteArrayFromObj(objv[1], &datalen);
(gdb) n
0x281a3558 in _init () from /usr/local/BerkeleyDB/lib/libdb_tcl.so
(gdb) n
Single stepping until exit from function _init,
which has no line number information.
berkdb_Cmd (notused=0x0, interp=0x804d420, objc=3, objv=0x8053020)
at ../dist/../tcl/tcl_db_pkg.c:73
73 printf("data is at 0x%x, contents 0x%x\n",data, (int)
*data);
(gdb) print data
$6 = 0x8079d28 "1", 'a' <repeats 23 times>
(gdb) x/4x 0x8079d28
0x8079d28: 0x61616131 0x61616161 0x61616161
0x61616161
(gdb) print *objv[1]
$7 = {refCount = 9, bytes = 0x805de60 "1", length = 1, typePtr =
0x280fe920,
internalRep = {longValue = 134716704, doubleValue =
1.2217634698305137e+161,
otherValuePtr = 0x8079d20, twoPtrValue = {ptr1 = 0x8079d20,
ptr2 = 0x61616161}}}
(gdb) n
0x281a3378 in _init () from /usr/local/BerkeleyDB/lib/libdb_tcl.so
(gdb) n
Single stepping until exit from function _init,
which has no line number information.
data is at 0x8079d28, contents 0x31
berkdb_Cmd (notused=0x0, interp=0x804d420, objc=3, objv=0x8053020)
at ../dist/../tcl/tcl_db_pkg.c:74
74 if (Tcl_GetIntFromObj(interp, objv[2], &id) != TCL_OK)
(gdb) n
0x281a39f8 in _init () from /usr/local/BerkeleyDB/lib/libdb_tcl.so
(gdb) n
Single stepping until exit from function _init,
which has no line number information.
berkdb_Cmd (notused=0x0, interp=0x804d420, objc=3, objv=0x8053020)
at ../dist/../tcl/tcl_db_pkg.c:76
76 printf("data is at 0x%x, contents 0x%x\n",data, (int)
*data);
(gdb) print data
$8 = 0x8079d28 "a"
(gdb) x/4x 0x8079d28
0x8079d28: 0x00000061 0x00000000 0x61616100
0x61616161
(gdb) print *objv[1]
$9 = {refCount = 9, bytes = 0x805de60 "1", length = 1, typePtr =
0x281006e4,
internalRep = {longValue = 1, doubleValue = 1.2217634361863799e+161,
otherValuePtr = 0x1, twoPtrValue = {ptr1 = 0x1, ptr2 =
0x61616161}}}
(gdb) quit
The program is running. Exit anyway? (y or n) y
build_unix 102 >> exit
build_unix 103 >> exit
Script done on Wed Aug 4 14:55:10 1999
DesiredBehavior:
I would have expected my objects to be discrete so that my conversion
to a byte array of objv[1] would not interfere with my int conversion of
objv[2], no matter what the contents of either.
Patch:
No patch. The only workaround I've come up with is careful
inspection of our code so that we rearrange it to have all byte
array operations done last so the memory remains valid.
PatchFiles:
N/A
This was not a bug, but an improper use of objects that are shared.
The programmer must either cache the 'data' of objv[1], check that
objv[1] != objv[2], or get the int first (since that automatically
stores the value in a separate int pointer).
-- 08/04/1999 hobbs