mirror of https://github.com/svaarala/duktape.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
201 lines
6.3 KiB
201 lines
6.3 KiB
--- !ditz.rubyforge.org,2008-03-06/issue
|
|
title: DUK_TVAL_SET_TVAL corruption in FreeBSD + clang
|
|
desc: |-
|
|
To reproduce:
|
|
|
|
* Compile in FreeBSD with clang 3.3:
|
|
|
|
$ clang -v
|
|
FreeBSD clang version 3.3 (tags/RELEASE_33/final 183502) 20130610
|
|
Target: x86_64-unknown-freebsd10.0
|
|
Thread model: posix
|
|
|
|
* Additional options: -m32, DUK_PROFILE=101
|
|
|
|
* Hand tune duk_features.h to enable all log levels
|
|
|
|
* The problem persists even when -fstrict-aliasing is NOT given, but
|
|
disappears if -Os is changed to -O0.
|
|
|
|
Incorrect behavior happens in multiple places (not sure if this is a clang/llvm
|
|
bug or incorrect assumptions in Duktape aliasing related code).
|
|
|
|
In duk_push_tval(), after DUK_TVAL_SET_TVAL(tv_slot, tv) has been called, the
|
|
memory dump of the two regions are (first byte is from 'tv', second is from
|
|
'tv_slot')::
|
|
|
|
60 60
|
|
32 32
|
|
81 81
|
|
28 28
|
|
00 00
|
|
00 00
|
|
f5 fd <==
|
|
ff ff
|
|
|
|
As can be seen, the second to last byte has been changed from F5 to FD, which
|
|
causes all sorts of problems. One possible culprit may be the DUK_TVAL_SET_TVAL()
|
|
macro itself, which simply uses *(dst) = *(src) for a duk_double_union - perhaps
|
|
this is non-portable. The problem persist even when replaced by a memmove() or
|
|
by (dst)->d = (src)->d.
|
|
|
|
Copying the values byte by byte using the 'uc' part of the union fixes this
|
|
particular problem::
|
|
|
|
#define DUK_TVAL_SET_TVAL(v,x) do { \
|
|
volatile int _i; \
|
|
for (_i = 0; _i < sizeof(duk_tval); _i++) { \
|
|
((unsigned char *) (v))[_i] = ((unsigned char *) (x))[_i]; \
|
|
} \
|
|
} while (0)
|
|
|
|
Note that if you remove "volatile", the fix works in most call sites but not
|
|
all! This suggests clang/llvm converts the hand-coded byte copy to some
|
|
intrinsic which fails to honor the aliasing rules for char pointers.
|
|
|
|
Another problem seems to happen in duk_hobject_props.c:realloc_props, at the
|
|
line:
|
|
|
|
new_e_pv[new_e_used] = DUK_HOBJECT_E_GET_VALUE(obj, i);
|
|
|
|
The line copies a duk_propvalue to another using a plain assignment. Debug
|
|
printing the results shows that the values get corrupted in the copy the same
|
|
way as in DUK_TVAL_SET_TVAL. Some values copy OK (first is source, second is
|
|
target):
|
|
|
|
00 00
|
|
00 00
|
|
00 00
|
|
00 00
|
|
00 00
|
|
00 00
|
|
f8 f8
|
|
7f 7f
|
|
|
|
00 00
|
|
00 00
|
|
00 00
|
|
00 00
|
|
00 00
|
|
00 00
|
|
f0 f0
|
|
7f 7f
|
|
|
|
while others get corrupted again at their 7th byte (little endian memory layout).
|
|
In more concrete terms, 0x08 gets OR'd to the 7th byte. Considering the double
|
|
as a 64-bit big endian value, 0x0008'0000'0000'0000 gets OR'd to the value:
|
|
|
|
2c 2c
|
|
cb cb
|
|
ff ff
|
|
ff ff
|
|
00 00
|
|
00 00
|
|
f1 f9 <==
|
|
ff ff
|
|
|
|
10 10
|
|
49 49
|
|
81 81
|
|
28 28
|
|
00 00
|
|
00 00
|
|
f6 fe <==
|
|
ff ff
|
|
|
|
The bit in question is the highest bit of the fractional part. This can be
|
|
similarly fixed by writing a manual byte-by-byte copy, again with a volatile
|
|
loop counter.
|
|
type: :bugfix
|
|
component: duk
|
|
release:
|
|
reporter: sva <sami.vaarala@iki.fi>
|
|
status: :unstarted
|
|
disposition:
|
|
creation_time: 2013-12-10 01:28:45.479389 Z
|
|
references: []
|
|
|
|
id: 4df1a3df6fcfc88b7515221dd083243df8747cb0
|
|
log_events:
|
|
- - 2013-12-10 01:28:45.647113 Z
|
|
- sva <sami.vaarala@iki.fi>
|
|
- created
|
|
- ""
|
|
- - 2013-12-10 01:30:44.318909 Z
|
|
- sva <sami.vaarala@iki.fi>
|
|
- commented
|
|
- |-
|
|
Below is a diff of the two fixes mentioned in the description, applied to
|
|
the all-in-one source, which together fix the crashes in FreeBSD + clang
|
|
3.3. The diff is not directly applicable because it is a bit hacky, but
|
|
illustrates the idea of the fix.
|
|
|
|
Removing the 'volatile' for either of the loop variables breaks the result.
|
|
|
|
$ diff -u duktape-orig.c duktape-fixed.c
|
|
--- duktape-orig.c 2013-12-10 04:28:08.000000000 +0200
|
|
+++ duktape-fixed.c 2013-12-10 04:28:15.000000000 +0200
|
|
@@ -5832,7 +5832,12 @@
|
|
#define DUK_TVAL_SET_BUFFER(v,h) DUK__TVAL_SET_TAGGEDPOINTER((v),(h),DUK_TAG_BUFFER)
|
|
#define DUK_TVAL_SET_POINTER(v,p) DUK__TVAL_SET_TAGGEDPOINTER((v),(p),DUK_TAG_POINTER)
|
|
|
|
-#define DUK_TVAL_SET_TVAL(v,x) do { *(v) = *(x); } while (0)
|
|
+#define DUK_TVAL_SET_TVAL(v,x) do { \
|
|
+ volatile int _i; \
|
|
+ for (_i = 0; _i < 8; _i++) { \
|
|
+ ((unsigned char *) (v))[_i] = ((unsigned char *) (x))[_i]; \
|
|
+ } \
|
|
+} while(0)
|
|
|
|
/* getters */
|
|
#define DUK_TVAL_GET_BOOLEAN(v) ((int) (v)->us[DUK_DBL_IDX_US1])
|
|
@@ -31536,7 +31541,15 @@
|
|
new_e_pv != NULL && new_e_f != NULL);
|
|
|
|
new_e_k[new_e_used] = key;
|
|
+#if 0
|
|
new_e_pv[new_e_used] = DUK_HOBJECT_E_GET_VALUE(obj, i);
|
|
+#endif
|
|
+ {
|
|
+ unsigned char *p_dst = (unsigned char *) &new_e_pv[new_e_used];
|
|
+ unsigned char *p_src = (unsigned char *) DUK_HOBJECT_E_GET_VALUE_PTR(obj, i);
|
|
+ volatile int _i;
|
|
+ for (_i = 0; _i < 8; _i++) { p_dst[_i] = p_src[_i]; }
|
|
+ }
|
|
new_e_f[new_e_used] = DUK_HOBJECT_E_GET_FLAGS(obj, i);
|
|
new_e_used++;
|
|
}
|
|
- - 2013-12-10 10:00:44.425108 Z
|
|
- sva <sami.vaarala@iki.fi>
|
|
- commented
|
|
- "See: misc/clang_aliasing.c"
|
|
- - 2013-12-10 18:19:25.100543 Z
|
|
- sva <sami.vaarala@iki.fi>
|
|
- commented
|
|
- |-
|
|
The root cause is the fact the clang ends up using a floating point load and
|
|
store to do the structured union assignment. A load+store sequence on X86
|
|
may convert a signaling NaN into a quiet NaN, which happens by setting the
|
|
highest bit of the mantissa. See, for instance:
|
|
|
|
http://caml.inria.fr/mantis/view.php?id=5038
|
|
|
|
This may be impossible to catch at compile time, so perhaps add a few self
|
|
tests and/or add an assert to DUK_TVAL_SET_TVAL. There is no telling what
|
|
other code might be affected though.
|
|
- - 2013-12-10 21:35:38.936057 Z
|
|
- sva <sami.vaarala@iki.fi>
|
|
- assigned to release v0.9 from v0.8
|
|
- ""
|
|
- - 2014-01-12 14:03:11.829583 Z
|
|
- sva <sami.vaarala@iki.fi>
|
|
- assigned to release v0.10 from v0.9
|
|
- ""
|
|
- - 2014-02-11 22:37:38.045628 Z
|
|
- sva <sami.vaarala@iki.fi>
|
|
- assigned to release v0.11 from v0.10
|
|
- ""
|
|
- - 2014-02-18 13:22:36.557661 Z
|
|
- sva <sami.vaarala@iki.fi>
|
|
- unassigned from release v0.11
|
|
- ""
|
|
|