Menu

#969 Error in use of ByteArray objects

final: 8.1.1
closed-invalid
nobody
2
2014-10-02
2000-10-26
Anonymous
No

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

Discussion

  • Brent B. Welch

    Brent B. Welch - 2000-10-26
    • priority: 5 --> 2
    • status: open --> closed-fixed
     
  • Don Porter

    Don Porter - 2001-04-22
    • labels: 104246 --> 12. ByteArray Object
    • status: closed-fixed --> closed-invalid