lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Hi,

I think I stumbled over a bug in the handling of strings in lstring.c or
lobject.h. Either that, or I'm doing something terribly wrong :)

I added a member to TString (an unsigned int, of which Lua needs to know
nothing about, as long as it keeps track of it), and now all calls to
svalue() seem to be broken, and I think I know why:

svalue() is defined as (lobject.h):

#define svalue(o)		getstr(tsvalue(o))
with
#define getstr(ts)      cast(const char *, (ts) + 1)

If I manually resolve this macro a bit, I get (barring calls to
check_exp)

#define svalue(o)		(const char *)(
&((o)->value.gc->ts.tsv)+ 1)

In other words, it returns (char *)((o)->value.gc->ts.tsv) +
sizeof(Tstring.stv), with o a TObject *, as the start of the actual
string.

However, it is initialized differently, see 

static TSTring *newlstr(lua_State *L, const char *str, size_t l,
unsigned int h) in lstring.c:

TString *ts;
[..]
ts = cast(TString *, luaM_malloc(L,
(l+1)*sizeof(char)+sizeof(TString)));
ts->tsv.len = l;
ts->tsv.hash = h;
ts->tsv.marked = luaC_white(G(L));
ts->tsv.tt = LUA_TSTRING;
ts->tsv.reserved = 0;
memcpy(ts+1, str, l*sizeof(char));
[..]
return ts;

In other words, the string starts at (char *) ts + sizeof(TString).
However, svalue expects it to be at (char *)ts.tsv +
sizeof(TString.tsv). This might not be the same!

TString was originally defined as follows:

typedef union TString {
  L_Umaxalign dummy;  /* ensures maximum alignment for strings */
  struct {
    CommonHeader;
    lu_byte reserved;
    unsigned int hash;
    size_t len;
  } tsv;
} TString;

On Microsoft Visual Studio 2005, this means that sizeof(TString) = 16
and sizeof(TString.tsv) = 16, and sizeof(TString.dummy) == 8. In this
case,
(char *) ts + sizeof(TString) == (char *) ts.tsv + sizeof(TString.tsv).

However, after I added another 32-bits int, the numbers changed due to
the required padding: sizeof(TString = 24, sizeof(TString.tsv) == 20,
with sizeof(TString.dummy) still at 8, so the above is no longer true;
the left hand side is actually 4 bytes further in memory.

I can "fix" things by adding another unsigned int to TString, but I
can't imagine that this is the right approach. Another option would be
to rewrite getstr(), so that it reads something like (cast(const char *,
(ts)) + sizeof(TString) but neither sounds very appealing. Maybe the
best option would be to rewrite the memcpy from newlstr to memcpy (
(char *)ts + sizeof(ts->tsv), ...)

Any suggestions?